1. 程式人生 > >Handler、Message、Loop訊息佇列模型,各部分的作用

Handler、Message、Loop訊息佇列模型,各部分的作用

Android系統的訊息佇列和訊息迴圈都是針對具體執行緒的,一個執行緒可以存在(當然也可以不存在)一個訊息佇列(Message Queue)和一個訊息迴圈(Looper)。Android中除了UI執行緒(主執行緒),建立的工作執行緒預設是沒有訊息迴圈和訊息佇列的。如果想讓該執行緒具有訊息佇列和訊息迴圈,並具有訊息處理機制,就需要線上程中首先呼叫Looper.prepare()來建立訊息佇列,然後呼叫Looper.loop()進入訊息迴圈。如以下程式碼所示:

class LooperThread extends Thread {   
      public Handler mHandler;   
  
      public void run() {   
          Looper.prepare();   
  
          mHandler = new Handler() {   
              public void handleMessage(Message msg) {   
                  // process incoming messages here   
              }   
          };   
  
          Looper.loop();   
      }   
  }  
 這樣該執行緒就具有了訊息處理機制了。如果不呼叫Looper.prepare()來建立訊息佇列,會報"Java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()"的錯誤。

通過下圖可以清晰顯示出UI Thread, Worker Thread, Handler, Massage Queue, Looper之間的關係:

解釋上圖中的幾個基本概念: 1.Message        訊息物件,顧名思義就是記錄訊息資訊的類。這個類有幾個比較重要的欄位:
       a.arg1和arg2:我們可以使用兩個欄位用來存放我們需要傳遞的整型值,在Service中,我們可以用來存放Service的ID。        b.obj:該欄位是Object型別,我們可以讓該欄位傳遞某個多項到訊息的接受者中。        c.what:這個欄位可以說是訊息的標誌,在訊息處理中,我們可以根據這個欄位的不同的值進行不同的處理,類似於我們在處理Button事件時,通過switch(v.getId())判斷是點選了哪個按鈕。     在使用Message時,我們可以通過new Message()建立一個Message例項,但是Android更推薦我們通過Message.obtain()或者Handler.obtainMessage()獲取Message物件。這並不一定是直接建立一個新的例項,而是先從訊息池中看有沒有可用的Message例項,存在則直接取出並返回這個例項。反之如果訊息池中沒有可用的Message例項,則根據給定的引數new一個新Message物件。通過分析原始碼可得知,Android系統預設情況下在訊息池中例項化10個Message物件。
2.MessageQueue        訊息佇列,用來存放Message物件的資料結構,按照“先進先出”的原則存放訊息。存放並非實際意義的儲存,而是將Message物件以連結串列的方式串聯起來的。MessageQueue物件不需要我們自己建立,而是有Looper物件對其進行管理,一個執行緒最多隻可以擁有一個MessageQueue。我們可以通過Looper.myQueue()獲取當前執行緒中的MessageQueue。 3.Looper        MessageQueue的管理者,在一個執行緒中,如果存在Looper物件,則必定存在MessageQueue物件,並且只存在一個Looper物件和一個MessageQueue物件。倘若我們的執行緒中存在Looper物件,則我們可以通過Looper.myLooper()獲取,此外我們還可以通過Looper.getMainLooper()獲取當前應用系統中主執行緒的Looper物件。在這個地方有一點需要注意,假如Looper物件位於應用程式主執行緒中,則Looper.myLooper()和Looper.getMainLooper()獲取的是同一個物件。 4.Handler        訊息的處理者。通過Handler物件我們可以封裝Message物件,然後通過sendMessage(msg)把Message物件新增到MessageQueue中;當MessageQueue迴圈到該Message時,就會呼叫該Message物件對應的handler物件的handleMessage()方法對其進行處理。由於是在handleMessage()方法中處理訊息,因此我們應該編寫一個類繼承自Handler,然後在handleMessage()處理我們需要的操作。        另外,我們知道,Android UI操作並不是執行緒安全的,所以無法在子執行緒中更新UI。但Andriod提供了幾種方法,可以在子執行緒中通知UI執行緒更新介面:
  • Activity.runOnUiThread( Runnable )
  • View.post( Runnable )
  • View.postDelayed( Runnable, long )
  • Handler
       比較常用的是通過Handler,用Handler來接收子執行緒傳送的資料,並用此資料配合主執行緒更新UI。那麼,只要在主執行緒中建立Handler物件,在子執行緒中呼叫Handler的sendMessage方法,就會把訊息放入主執行緒的訊息佇列,並且將會在Handler主執行緒中呼叫該handler的handleMessage方法來處理訊息。 
package com.superonion;   
  
import android.app.Activity;   
import android.os.Bundle;   
import android.os.Message;   
import android.util.Log;   
import android.os.Handler;   
  
public class MyHandler extends Activity {   
    static final String TAG = "Handler";   
    Handler h = new Handler(){   
        public void handleMessage (Message msg)   
        {   
            switch(msg.what)   
            {   
            case HANDLER_TEST:   
                Log.d(TAG, "The handler thread id = " + Thread.currentThread().getId() + "\n");   
                break;   
            }   
        }   
    };   
  
    static final int HANDLER_TEST = 1;   
    /** Called when the activity is first created. */  
    @Override  
    public void onCreate(Bundle savedInstanceState) {   
        super.onCreate(savedInstanceState);   
        Log.d(TAG, "The main thread id = " + Thread.currentThread().getId() + "\n");   
  
        new myThread().start();   
        setContentView(R.layout.main);   
    }   
  
    class myThread extends Thread   
    {   
        public void run()   
        {   
            Message msg = new Message();   
            msg.what = HANDLER_TEST;   
            h.sendMessage(msg);   
            Log.d(TAG, "The worker thread id = " + Thread.currentThread().getId() + "\n");   
        }   
    }   
}  

     以上程式碼中,Handler在主執行緒中建立後,子執行緒通過sendMessage()方法就可以將訊息傳送到主執行緒中,並在handleMessage()方法中處理。 注意:由於Handler為匿名內部類,預設是引用外部例項的。所以我上面的程式碼寫法是有問題的,正確的寫法應該把Handler生明成一個靜態內部類繼承自Handler。這裡由於篇幅原因就不再贅述了。如果感興趣可以檢視我之前的一篇關於Android記憶體洩漏的一篇文章,連結如下:http://blog.csdn.net/longlyboyhe/article/details/48809401 總結:
  • Handler通過sendMessage()傳送Message到MessageQueue佇列;
  • Looper通過loop(),不斷提取出達到觸發條件的Message,並將Message交給target來處理;
  • 經過dispatchMessage()後,交回給Handler的handleMessage()來進行相應地處理。
  • 將Message加入MessageQueue時,處往管道寫入字元,可以會喚醒loop執行緒;如果MessageQueue中沒有Message,並處於Idle狀態,則會執行IdelHandler介面中的方法,往往用於做一些清理性地工作。