Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android:異步處理之Handler+Thread的應用(一)

Android:異步處理之Handler+Thread的應用(一)

編輯:關於Android編程

前言     很久很久以前就聽說了,每一個android的應用程序都會分別運行在一個獨立的dalvik虛擬機進程中,而在每個虛擬機在啟動時會運行一個UI主線程(Main Thread),而為啥叫UI主線程而不是AI主線程或者是BI主線程呢?因為它要處理全部和UI相關的事件;因為Android系統采用的是UI單線程模型,只能由UI主線程對其進行UI操作,如果子線程抱著眾人拾柴火焰高的覺悟來幫忙UI主線程更新UI界面的話,對不起哦~Android系統就會報錯的。粗俗點講就是:我們只能通過UI主線程來蹂躏UI界面,但是其他線程來的話會被告弓雖女干滴。。     那麼現在問題來了!鑒於近來挖掘機那麼火,我也不好意思繼續問這個問題了。。。嗯嗯~網絡操作之類耗時操作就像挖掘機那樣,我們在下載文件的時候一樣跟挖掘機挖個大坑一樣需要一定的時間;當挖掘機司機挖好一個大坑要找老板反饋工作完成一樣,我們下載好一個文件自然要馬上告訴屏幕前苦逼等待的用戶們,誰知道他們多著急想看**.avi呢;但是你在挖坑時好意思叫老板在旁邊看你嗎?老板分分鐘為幾千萬上下的事忙著呢~所以嘛同理,對於網絡操作,我們當然也不能在UI主線程中進行網絡操作,因為這樣會阻塞主線程造成界面卡死,也會造成ANR(應用程序無響應)。我們應該把文件下載、文件讀取諸如此類的耗時操作放到子線程中去進行,等到子線程耗時操作完成時通知UI界面做出響應。   不要在UI主線程中進行耗時操作     如果你不信邪一定要在UI主線程進行下載文件、加載大文件之類的耗時操作。如下代碼:   復制代碼 private Button btn; //onCreate之類的生命周期的方法就是允許在UI主線程中 @Override protected void onCreate(Bundle savedInstanceState) {   super.onCreate(savedInstanceState);   setContentView(R.layout.activity_main);            btn = (Button) findViewById(R.id.btn);            btn.setOnClickListener(new OnClickListener() {     @Override     public void onClick(View v) {       downLoad();//調用UI主線程的下載函數     }   }); }      private void downLoad(){   try {     Thread.sleep(10000);//休眠10秒,模擬網絡文件下載耗時操作   } catch (InterruptedException e) {     e.printStackTrace();   } } 復制代碼 你會發現界面卡主了10秒:(模擬下載操作的按鈕為深色,說明按鈕一直為按下狀態)       如果這時候你手比較管不住的話,雖然點幾下界面,沒事~Androi系統會馬上送你一份ANR大禮哦,而且還不用998元耶!       小結一個:不要在UI主線程中進行耗時操作,你可能會疑問什麼是UI主線程,UI主線程主要運行的就是Activity、Service等裡面的生命周期方法,所以不要在生命周期方法如onCreate()中進行下載這些大事件。對於耗時操作,我們應該新建一個子線程並交給他處理,但是還需要注意一點。   不要在子線程中更新UI界面     既然我們說下載文件要在子線程中進行,那麼我們就新建一個子線程把下載操作放到裡面進行咯,代碼如下:   復制代碼 protected void onCreate(Bundle savedInstanceState) {   super.onCreate(savedInstanceState);   setContentView(R.layout.activity_main);            btn = (Button) findViewById(R.id.btn);   text = (TextView) findViewById(R.id.text);            btn.setOnClickListener(new OnClickListener() {     @Override     public void onClick(View v) {       new Thread(){         @Override         public void run() {           //在子線程中進行下載操作           try {             Thread.sleep(10000);//休眠10秒,模擬耗時操作           } catch (InterruptedException e) {             e.printStackTrace();           }           text.setText("下載完成");//設置TextView,通知UI界面下載完成         }       }.start();     }   }); } 復制代碼 10秒後,你覺得會在UI界面完美顯示“下載完成”麼?一般,出現這個才符合Androi系統的一貫作風       並且在Log中報錯如下       小弟英語其實很廢柴,但是隱隱約約有人告訴我:這不是叫只能在主線程中更新UI嗎?不信,金山翻譯一下去呀。。。。   小結一個:不要在子線程中更新UI界面,這樣會導致android系統報錯、應用崩潰退出。UI界面時單線程模式,我們只能通過UI主線程中對UI的界面進行相關的更新,千萬不要越線辦事,你要記住的是~UI界面是UI主線程的老婆,你們這些子線程誰都別想動!   利用Thread+Handler進行異步處理     那麼問題來了,現在我們需要進行耗時操作(例如下載文件)時不能在主線程執行,我們又需要在UI界面通知用戶我們活干完了不能再子線程中執行。這似乎是一個棘手的熱山芋呀,幸好谷歌給我們提供了一個救我們於危難之中的Handler,一個能讓主線程監聽子線程發送來消息的東東,至於Handler的實現原理我會在後面的文章詳細介紹,現在我們只需要先了解Handler的用法。   復制代碼 private Button btn; private TextView text;      private Handler handler = new Handler(){   private int process = 0;   @Override   public void handleMessage(Message msg) {     switch(msg.what){     case 0://更細下載進度       process += 1;       text.setText("下載" + process + "%");//在主線程中更新UI界面       break;     case 1://提示下載完成       text.setText("下載完成");//在主線程中更新UI界面       break;     default:       break;     }   } }; //onCreate之類的生命周期的方法就是允許在UI主線程中 @Override protected void onCreate(Bundle savedInstanceState) {   super.onCreate(savedInstanceState);   setContentView(R.layout.activity_main);            btn = (Button) findViewById(R.id.btn);   text = (TextView) findViewById(R.id.text);            btn.setOnClickListener(new OnClickListener() {   @Override   public void onClick(View v) {     new Thread(){       @Override       public void run() {         //在子線程中進行下載操作         for(int i = 0; i < 100; i++){           try {             Thread.sleep(200);//休眠0.2秒,模擬耗時操作           } catch (InterruptedException e) {             e.printStackTrace();           }           handler.sendEmptyMessage(0);//發送消息到handler,通知下載進度         }         handler.sendEmptyMessage(1);//發送消失到handler,通知主線程下載完成         }       }.start();     }   }); } 復制代碼  這裡來解釋一下Handler的使用方法:   1、我們為了不阻塞主線程,將下載任務通過子線程來執行。   復制代碼 new Thread(){   @Override   public void run() {     //在子線程中進行下載操作     for(int i = 0; i < 100; i++){       try {         Thread.sleep(200);//休眠0.2秒,模擬耗時操作       } catch (InterruptedException e) {         e.printStackTrace();       }       handler.sendEmptyMessage(0);//發送消息到handler,通知下載進度     }     handler.sendEmptyMessage(1);//發送消失到handler,通知主線程下載完成   } }.start(); 復制代碼 2、當子線程需要跟主線程交流時,也就是當子線程要跟UI主線程說:親,偶下載文件到80%了或者偶已經把文件下載完成了!執行這句代碼   handler.sendEmptyMessage(1);//發送消失到handler,通知主線程下載完成 3、當發送空消息之後,在Handler將會收到子線程發來的消息,觸發回調方法handlerMessage(),我們就在這裡對UI界面進行更新,這個回調方法是運行在UI主線程的   復制代碼 @Override public void handleMessage(Message msg) {   switch(msg.what){   case 0://更細下載進度     process += 1;     text.setText("下載" + process + "%");//在主線程中更新UI界面     break;   case 1://提示下載完成     text.setText("下載完成");//在主線程中更新UI界面     break;   default:     break;   } } 復制代碼 4、最後,UI界面更新成功!(圖嘛,我這裡就不上了。。。。)   小結一個:對於比較耗時間的任務,我們一般需要放在子線程中執行;當子線程更新UI界面時,子線程可以通過Handler來通知主線程更新,一般通過發送消息來觸發handlerMessage()這個回調方法來執行UI界面的更新。   進一步簡略de操作:handler.post方法和view.post方法     但是如果你覺得每次都要重寫handlerMessage()比較麻煩,我們完全可以用更加簡略的方法來解決我們的需求,就是用handler中的post方法。代碼如下   復制代碼 new Thread(){   @Override   public void run() {     //在子線程中進行下載操作     try {       Thread.sleep(1000);     } catch (InterruptedException e) {       e.printStackTrace();     }     handler.post(new Runnable() {       @Override       public void run() {         text.setText("下載完成");       }     });//發送消失到handler,通知主線程下載完成   } }.start(); 復制代碼   這樣處理的話我們就可以不用重寫handlerMessage()方法了,適合子線程與主線程進行較為單一的交流。但在這裡我們要強調的一點的是,post裡面的Runnable還是在UI主線程中運行的,而不會另外開啟線程運行,千萬不要在Runnable的run()裡面進行耗時任務,不然到時又ANR了可別找我哦。。   如果你有時候連handler都不想搞,還可以這樣寫代碼滴。   我們只需要把handler換成View組件進行post,更新任務自然會加載到UI主線程中進行處理。   復制代碼 text.post(new Runnable() {   @Override   public void run() {     text.setText("下載完成");   } });//發送消失到handler,通知主線程下載完成 復制代碼 至於Handler機制以及這兩種post的原理,我將會在後面的博客文章中專題介紹,這裡只提供一個使用方法而已。
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved