Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android的消息處理機制——Looper,Handler和Message淺析

Android的消息處理機制——Looper,Handler和Message淺析

編輯:關於Android編程

題外話:

說來有些慚愧,對於這三者的初步認識居然是在背面試題的時候。那個時候自己接觸Android的時間還不長,學習的書籍也就是比較適合入門的《瘋狂Android講義》,當然在學到Handler這一部分的時候,書中也是有提到一些簡單示例,後來在工作中需要用到這個MessageQueue的時候才開始真正琢磨了一下這三者的聯系。如果想要對這三者好好理解一番,個人還是比較推薦《深入理解Android卷Ⅰ》。以下對這三者之間的恩怨糾葛的介紹和分析也是參考這本書的相關章節,算是一篇讀書筆記吧。

 

概述:

Android的消息傳遞機制是另一種形式的“事件處理”,這種機制主要是為了解決Android應用中的多線程問題——Android平台只允許UI線程修改Activity中的UI組件,這就使得新啟動的線程無法去動態修改界面組件中的屬性值。但是我們的程序界面不可能是一個靜態的呈現,所以這就必須用到本博客中提到的三個大類了。

 

簡單示例:

代碼展示:

 

public class LooperThreadActivity extends Activity {

    private final int MSG_HELLO = 0;
    private Handler mHandler;
    private CustomThread mThread = null;
    private static final String TAG = LooperThreadActivity.class.getSimpleName();
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        mThread = new CustomThread();
        mThread.start();
        
        Button sendButton = (Button) findViewById(R.id.send_button);
        final EditText contentEditText = (EditText) findViewById(R.id.content_edittext);

        sendButton.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                String msgText = contentEditText.getText().toString();
                sendMessage(msgText);
            }
        });
    }
    
    private void sendMessage(String content) {
        Toast.makeText(this, send msg:  + content, 0).show();
        
        // TODO 1.向MessageQueue中添加消息
        
        // 通過Message.obtain()來從消息池中獲得空消息對象,以節省資源
        Log.i(TAG, ------------> send msg 1.);
        Message msg = mHandler.obtainMessage(MSG_HELLO, content);
        msg.sendToTarget();
        
        Log.i(TAG, ------------> send msg 2.);
        Message msg2 = mHandler.obtainMessage(MSG_HELLO, content + 2);
        msg2.sendToTarget();
    }

    class CustomThread extends Thread {
        @Override
        public void run() {
            Looper.prepare();
            Log.i(TAG, ------------> loop.pre.);
            
            mHandler = new Handler() {
                public void handleMessage(Message msg) {
                    switch (msg.what) {
                    case MSG_HELLO:
                        Log.i(TAG, ------------> receive msg.);
                        Toast.makeText(LooperThreadActivity.this, receive msg:  + (String) msg.obj, 0).show();
                    }
                }
            };
            Looper.loop();
        }
    }
}

 

運行效果展示:

\ \

 

Log結果展示:

\

 

示例結果分析:

大家可以看到我做了連續兩次的添加消息數據,在結果中也有很好的體現,不過Looper.prepare();和Handler之間的內容卻只執行了一次。這是因為我們自定義的線程CustomThread只被start了一次,且start過後一直存在,沒有被銷毀,所以Looper一直存在,MessageQueue一直存在,從而保證了一個Thread只能有一個Looper對象。對於這一點下面會用源碼進行進一步的說明。

 

機制淺析:

就應用程序而言,Android系統中Java的應用程序和其他系統上相同,都是靠消息驅動來工作的。Android系統中的消息驅動離不開Looper、Handler和Message這三者,雖說不上哪個更重要一些,不過相對突出的的確是Looper。下面就對這些類逐一地介紹。

Looper類分析:

1.Looper.prepare();

跟蹤prepare()進入Android的源碼,我們可以發現以下源代碼:

 

private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException(Only one Looper may be created per thread);
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

 

sThreadLocal定義:

// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal sThreadLocal = new ThreadLocal();

從以上源碼中我們可以看到,在調用prepare的線程中,設置了一個Looper對象,這個Looper對象就保存在這個調用線程的TLV中。而Looper對象內部封裝了一個消息隊列。也就是說prepare通過ThreadLocal機制,把Looper和調用線程關聯在了一起。
 

2.Looper.loop();

跟蹤loop()進入Android的源碼(此處刪除了一些暫時不太關聯的代碼):

 

public static void loop() {
	final Looper me = myLooper();
    if (me == null) {
        throw new RuntimeException(No Looper; Looper.prepare() wasn't called on this thread.);
    }
    final MessageQueue queue = me.mQueue;

    Binder.clearCallingIdentity();
    final long ident = Binder.clearCallingIdentity();

    for (;;) {
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }

        msg.target.dispatchMessage(msg);

        msg.recycle();
    }
}
通過上面的分析,Looper有以下幾個作用:

 

 

封裝了一個消息隊列.prepare函數把當前的Looper和調用prepare的線程(即最終的處理線程)綁定在了一起.處理線程調用loop,處理來自該消息隊列中的消息. 當事件源向這個Looper發送消息的時候,其實就是把消息加到這個Looper的消息列隊裡了。那麼,該消息就將由和Looper綁定的處理來處理。

 

 

Handler類分析:

學習Handler之初先來認識一下Handler中所包含的部分成員:

 

final MessageQueue mQueue;
final Looper mLooper;
final Callback mCallback;
在Handler類中,它的構造函數會把Handler中的消息隊列變量最終都會指向Looper的消息隊列。由於是被指向,那麼Handler中的消息隊列其實就是某個Looper的消息隊列。

 

Looper和Handler的同步關系說明及HandlerThread的介紹:

Looper和Handler之間其實是存在著同步關系的。這裡對它們之間的同步關系不做過多介紹,如果想了解可以參看《深入理解Android卷Ⅰ》第128頁。筆者在此只提出一個提醒點:由於HandlerThread完美地解決了Looper和Handler同步過程中可能出現的空指針異常問題,所以在以後的開發過程中,我們還是多用HandlerThread吧。當然如果不想使用它,那就請使用鎖機制來健壯你的代碼吧,不過這就可能會落下重復造輪子的口舌了。

 

 

尾聲:

雖然已經“稀裡糊塗”到了結尾,不過對Looper、Handler和Message的認識的確進了一大步。希望看完本文的你也有所收獲。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved