【轉載】Android中UI線程與後台線程互動設計的5種方法

來源:互聯網
上載者:User

標籤:start   int   bin   oop   互動   調用exe   基於   led   會同   

原帖地址:http://www.cr173.com/html/19165_1.html

在android的設計思想中,為了確保使用者順滑的操作體驗。一些耗時的任務不能夠在UI線程中運行,像訪問網路就屬於這類任務。因此我們必須要重新開啟一個後台線程運行這些任務。然而,往往這些任務最終又會直接或者間接的需要訪問和控制UI控制項。例如訪問網路擷取資料,然後需要將這些資料處理顯示出來。就出現了上面所說的情況。原本這是在正常不過的現象了,但是android規定除了UI線程外,其他線程都不可以對那些UI控制項訪問和操控。為瞭解決這個問題,於是就引出了我們今天的話題。Android中後台線程如何與UI線程互動。

據我所知android提供了以下幾種方法,用於實現後台線程與UI線程的互動。

1、handler

2、Activity.runOnUIThread(Runnable)

3、View.Post(Runnable)

4、View.PostDelayed(Runnabe,long)

5、AsyncTask

方法一:handler

handler是android中專門用來線上程之間傳遞資訊類的工具。

要講明handler的用法非常簡單,但是我在這裡會少許深入的講一下handler的運行機制。

為了能夠讓handler線上程間傳遞訊息,我們還需要用到幾個類。他們是looper,messageQueue,message。

這裡說的looper可不是前段時間的好萊塢大片環形使者,他的主要功能是為特定單一線程運行一個訊息環。一個線程對應一個looper。同樣一個looper對應一個線程。這就是所謂的特定單一。一般情況下,在一個線程建立時他本身是不會生產他特定單一的looper的(主線程是個特例)。因此我們需要手動的把一個looper與線程相關聯。其方法只需在需要關聯的looper的線程中調用Looper.prepare。之後我們再調用Looper.loop啟動looper。

說了這麼多looper的事情,到底這個looper有什麼用哪。其實之前我們已經說到了,他是為線程運行一個訊息環。具體的說,在我們將特定單一looper與線程關聯的時候,looper會同時生產一個messageQueue。他是一個訊息佇列,looper會不停的從messageQuee中取出訊息,也就是message。然後線程就會根據message中的內容進行相應的操作。

那麼messageQueue中的message是從哪裡來的哪?那就要提到handler了。在我們建立handler的時候,我們需要與特定的looper綁定。這樣通過handler我們就可以把message傳遞給特定的looper,繼而傳遞給特定的線程。在這裡,looper和handler並非一一對應的。一個looper可以對應多個handler,而一個handler只能對應一個looper(突然想起了一夫多妻制,呵呵)。這裡補充一下,handler和looper的綁定,是在構建handler的時候實現的,具體查詢handler的建構函式。

在我們建立handler並與相應looper綁定之後,我們就可以傳遞message了。我們只需要調用handler的sendMessage函數,將message作為參數傳遞給相應線程。之後這個message就會被塞進looper的messageQueue。然後再被looper取出來交給線程處理。

這裡要補充說一下message,雖然我們可以自己建立一個新的message,但是更加推薦的是調用handler的obtainMessage方法來擷取一個message。這個方法的作用是從系統的訊息池中取出一個message,這樣就可以避免message建立和銷毀帶來的資源浪費了(這也就是算得上重複利用的綠色之舉了吧)。

突然發現有一點很重要的地方沒有講到,那就是線程從looper收到message之後他是如何做出響應的嘞。其實原來線程所需要做出何種響應需要我們在我們自訂的handler類中的handleMessage重構方法中編寫。之後才是之前說的建立handler並綁定looper。

好吧說的可能喲點亂,總結一下利用handler傳遞資訊的方法。

假設A線程要傳遞資訊給B線程,我們需要做的就是

1、在B線程中調用Looper.prepare和Looper.loop。(主線程不需要)

2、 編寫Handler類,重寫其中的handleMessage方法。

3、建立Handler類的執行個體,並綁定looper

4、調用handler的sentMessage方法發送訊息。

到這裡,我們想handler的運行機制我應該是闡述的差不多了吧,最後再附上一段代碼,供大家參考。

 1 public class MyHandlerActivity extends Activity { 2      TextView textView; 3      MyHandler myHandler; 4   5      protected void onCreate(Bundle savedInstanceState) { 6          super.onCreate(savedInstanceState); 7          setContentView(R.layout.handlertest); 8   9          //實現建立handler並與looper綁定。這裡沒有涉及looper與            //線程的關聯是因為主線程在建立之初就已有looper10          myHandler=MyHandler(MyHandlerActivitythis.getMainLooper());11          textView = (textView) findViewById(R.id.textView);12         13          MyThread m = new MyThread();14          new Thread(m).start();15      }16  17  18      class MyHandler extends Handler {19          public MyHandler() {20          }21  22          public MyHandler(Looper L) {23              super(L);24          }25  26          // 必須重寫這個方法,用於處理message27          @Override28          public void handleMessage(Message msg) {29              // 這裡用於更新UI30              Bundle b = msg.getData();31              String color = b.getString("color");32              MyHandlerActivity.this.textView.setText(color);33          }34      }35  36      class MyThread implements Runnable {37          public void run() {38              //從訊息池中取出一個message39              Message msg = myHandler.obtainMessage();40              //Bundle是message中的資料41              Bundle b = new Bundle();42              b.putString("color", "我的");43              msg.setData(b);44              //傳遞資料45              myHandler.sendMessage(msg); // 向Handler發送訊息,更新UI46          }47      }

方法二:Activity.runOnUIThread(Runnable)

 這個方法相當簡單,我們要做的只是以下幾步

1、編寫後台線程,這回你可以直接調用UI控制項

2、建立後台線程的執行個體

3、調用UI線程對應的Activity的runOnUIThread方法,將後台線程執行個體作為參數傳入其中。

注意:無需調用後台線程的start方法

方法三:View.Post(Runnable)

 該方法和方法二基本相同,只是在後台線程中能操控的UI控制項被限制了,只能是指定的UI控制項View。方法如下

1、編寫後台線程,這回你可以直接調用UI控制項,但是該UI控制項只能是View

2、建立後台線程的執行個體

3、調用UI控制項View的post方法,將後台線程執行個體作為參數傳入其中。

方法四:View.PostDelayed(Runnabe,long)

該方法是方法三的補充,long參數用於制定多少時間後運行後台進程 

方法五:AsyncTask

AsyncTask是一個專門用來處理後台進程與UI線程的工具。通過AsyncTask,我們可以非常方便的進行後台線程和UI線程之間的交流。

那麼AsyncTask是如何工作的哪。

AsyncTask擁有3個重要參數

1、Params 

2、Progress

3、Result

Params是後台線程所需的參數。在後台線程進行作業的時候,他需要外界為其提供必要的參數,就好像是一個用於下載圖片的後台進程,他需要的參數就是圖片的。

Progress是後台線程處理作業的進度。依舊上面的例子說,就是下載圖片這個任務完成了多少,是20%還是60%。這個數字是由Progress提供。

Result是後台線程啟動並執行結果,也就是需要提交給UI線程的資訊。按照上面的例子來說,就是下載完成的圖片。

AsyncTask還擁有4個重要的回調方法。

1、onPreExecute

2、doInBackground

3、onProgressUpdate

4、onPostExecute

onPreExecute運行在UI線程,主要目的是為後台線程的運行做準備。當他運行完成後,他會調用doInBackground方法。

doInBackground運行在後台線程,他用來負責運行任務。他擁有參數Params,並且返回Result。在後台線程的運行當中,為了能夠更新作業完成的進度,需要在doInbackground方法中調用PublishProgress方法。該方法擁有參數Progress。通過該方法可以更新Progress的資料。然後當調用完PublishProgress方法,他會調用onProgressUpdate方法用於更新進度。

onProgressUpdate運行在UI線程,主要目的是用來更新UI線程中顯示進度的UI控制項。他擁有Progress參數。在doInBackground中調用PublishProgress之後,就會自動調onProgressUpdate方法

onPostExecute運行在UI線程,當doInBackground方法運行完後,他會調用onPostExecute方法,並傳入Result。在onPostExecute方法中,就可以將Result更新到UI控制項上。

明白了上面的3個參數和4個方法,你要做的就是

1、編寫一個繼承AsyncTask的類,並聲明3個參數的類型,編寫4個回調方法的內容。

2、然後在UI線程中建立該類(必須在UI線程中建立)。

3、最後調用AsyncTask的execute方法,傳入Parmas參數(同樣必須在UI線程中調用)。

這樣就大功告成了。

另外值得注意的2點就是,千萬不要直接調用那四個回調方法。還有就是一個AsyncTask執行個體只能執行一次,否則就出錯哦。

以上是AsyncTask的基本用法,更加詳細的內容請參考android官方文檔。最後附上一段代碼,供大家參考。

 1 private class DownloadFilesTask extends AsyncTask<URL, Integer, Long>  2 //在這裡聲明了Params、Progress、Result參數的類型 3 { 4     //因為這裡不需要使用onPreExecute回調方法,所以就沒有加入該方法 5      6     //後台線程的目的是更具URL下載資料 7      protected Long doInBackground(URL... urls) { 8          int count = urls.length;//urls是數組,不止一個下載連結 9          long totalSize = 0;//下載的資料10          for (int i = 0; i < count; i++) {11              //Download是用於下載的一個類,和AsyncTask無關,大家可以忽略他的實現12              totalSize += Downloader.downloadFile(urls[i]);13              publishProgress((int) ((i / (float) count) * 100));//更新下載的進度14              // Escape early if cancel() is called15              if (isCancelled()) break;16          }17          return totalSize;18      }19 20      //更新下載進度21      protected void onProgressUpdate(Integer... progress) {22          setProgressPercent(progress[0]);23      }24 25      //將下載的資料更新到UI線程26      protected void onPostExecute(Long result) {27          showDialog("Downloaded " + result + " bytes");28      }29  }30

 有了上面的這個類,接下你要做的就是在UI線程中建立執行個體,並調用execute方法,傳入URl參數就可以了。

這上面的5種方法各有優點。但是究其根本,其實後面四種方法都是基於handler方法的封裝。在一般的情形下後面四種似乎更值得推薦。但是當情形比較複雜,還是推薦使用handler。

最後補充一下,這是我的第一篇部落格。存在很多問題請大家多多指教。尤其是文中涉及到內容,有嚴重的技術問題,大家一定要給我指明啊。拜託各位了。

【轉載】Android中UI線程與後台線程互動設計的5種方法

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.