Android中的消息隊列與消息循環:認識Handler、Looper

    學習android到今天也將近3個月了,雖然各類界面各類組件一直在歡快的用着,但有一天突然發現,一些簡單的東西去不是很理解:好比Bundle和Handler。因而就抽空好好閱讀了下adk文檔。如今也算是理解一二了,技術須要分享,因而乎花了半天的時間整理了這篇文章。html

一、Bundle(打醬油的)

這應該是任何一個學習android的人建立的第一個hello world程序都會遇到的一個類,它是activity的oncreate的入參,但因爲作東西的時候不多會遇到它,也就一直沒在乎。java

這個類很簡單,先聽聽官方的解釋:「A mapping from String values to various Parcelable types.」android

Soga,原來就是一個String - Object的映射表,同時又是支持序列化的。windows

再看代碼:「final class Bundle implements Parcelable, Cloneable 」。實現了兩個接口,前者是序列化接口,後置是淺拷貝和深拷貝的接口,具體實現就再也不說了,這個不是重點。網絡

總之一句話,Bundle就是爲了實現數據傳輸而生的,好比activity之間。一般Bundle會附在intent裏面intent.putExtra()。app

好了,打住,進入下一個議題:消息循環!異步

二、Windows消息循環

作過客戶端的確定都會接觸到一個概念:UI線程。說白了,UI線程和普通工做線程的區別就在於消息循環。消息循環就是用來處理分發相關線程消息隊列中的消息,Android和Windows都是如此。做爲一名曾經的windows開發人員,研究的第一個點也是系統對UI的消息處理流程,否則幹活都沒底氣。ide

int _WinMain(args ...)
{
    //WND definition ... ...
     while (GetMessage(&msg, NULL, 0, 0))
     {            
     TranslateMessage(&msg); 
     DispatchMessage(&msg); 
   }
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)  
{
    switch(message)
    {
        ... ...
    }
}


上面就是通過簡化後的一個最基本的windows的建立窗口的過程。其中main函數裏面的while就是一個消息循環,來不斷從消息隊列裏面讀取消息並dispatch消息。dispatch的消息均會進入到消息處理回調函數WndProc中完成消息的處理。這個程序的main線程也就是一般說的UI線程。假如咱們另起一個線程包含了上面的while循環,則那個線程也照樣能夠建立窗口什麼的,那麼它也是一個UI線程。
函數

若是對win32的ui感興趣,能夠參考http://blog.csdn.net/cheneywong/article/details/8847810oop

上面說了這麼些的windows相關技術,其實就是想描述下窗口處理的基本原理,由於有些概念在windows上更易懂寫,這樣再看android的時候容易理解些。


三、Android的Looper

官方解釋:Class used to run a message loop for a thread. Threads by default do not have a message loop associated with them; to create one, call prepare() in the thread that is to run the loop, and then loop() to have it process messages until the loop is stopped.

很明白,looper就是一個用來處理消息的消息循環。默認的Thread也是不具備處理消息的能力,須要添加Looper來實現。說白了,android中的UI線程也就是帶有一個Looper的Thread,用戶全部的操做產生的消息均經過Looper來分發處理(這句沒有看代碼驗證,但應該也是八九不離十了)。

和上一節比較咱們發現好像少了一點東西 -- 消息處理回調函數WndProc。

Most interaction with a message loop is through the Handler class.」

這句話也引出了Looper和Hanlder的關係了。一個消息循環分發消息,一個消息處理。以下一段實例代碼,演示了線程中添加消息循環並處理的過程:

  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();
      }
  }


看了這段代碼,我就馬上產生了一個疑問,windows中的wndproc是須要註冊才能進入處理的,那上段代碼handler沒有註冊卻能接收並處理消息!!

四、Android的Handler

官方解釋:A Handler allows you to send and process Message and Runnable objects associated with a thread's MessageQueue. Each Handler instance is associated with a single thread and that thread's message queue. When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it -- from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.
 簡單翻譯:Handler容許你向線程的MessageQueue發送消息,同時還可以處理該消息隊列裏的Message和Runnable。每一個handler只能關聯一個線程,並在建立的時候和線程綁定。


原來建立Hanlder的時候,它會本身綁定到線程和消息隊列上,這就解釋了上一節的疑問了。


另外值得注意的一條規則:每一個Handler只會和一個Thread綁定,即建立它的Thread。後面會講到這條規則的用處。

這裏面又引入了兩個概念:Message和向消息隊列發送消息。

Message

消息體,查看源碼大體包含了以下幾個核心成員:

Bundle date ;        // 消息體包含的數據

Handler  target;    // 消息對應的「target", 即由誰來處理

Runable callback; // 消息處理的回調

Message next;      // 下一條消息

五、再看Looper

Looper的結構相對簡單,主要有如下兩個成員

{

    final MessageQueue mQueue;

    final Thread mThread;

}

經過成員變量,咱們能夠看出一個消息循環Looper均和一個Thread相關聯,同時還會附帶一個消息隊列MessageQueue。

另外,還有兩個靜態成員:

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

該對象能夠理解成管理Thread和Looper的關係就行了。

private static Looper sMainLooper; 

主線程的Looper。這樣,任何就能夠在任何線程獲取到主線程(UI線程)的消息循環體了,進而能夠向UI線程發送消息。

最後,咱們再看下以前代碼中出現的Looper.prepare()和Looper.loop()。這兩個函數均是靜態函數


prepare()

經過ThreadLocal建立Looper。


loop():

簡化後的僞代碼大體以下:

public static void loop() {
    loop = getThisThreadLooper();
    queue = loop.mQueue;
    for(;;){
        msg = queue.next();
        // 分發消息
        msg.target.dispatchMessage(msg);
        msg.recycle();
    }
}

原來loop函數也就是一個無線循環,不斷的從消息隊列裏面取出消息並交給Message中的目標Handler處理。好了,這就清晰了,消息循環原來如此簡單。


附加:prepareMainLooper

    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

該函數顧名思義,就是來準備主線程(UI線程)的消息循環,代碼的註釋中也說明了:該函數是初始化application的主looper。該函數有android系統來調用,咱們不該該本身來調用,否則會拋出異常。


六、再看Handler

上一節中出現了一個函數Handler.dispatchMessage(Message).  下面咱們也會重點分析該函數的。

{
    final MessageQueue mQueue;
    final Looper mLooper;
    final Callback mCallback;
}

Handler中的成員主要有上面3個,消息隊列、消息循環和回調Callback。


(1)初始化

    獲取建立本身的線程的消息循環以及消息隊列:mLooper = Looper.myLooper();

    另外用戶能夠爲Handler指定一個Callback 。

(2)Callback 接口和handleMessage成員方法

    Handler內部定義了以下接口:

public interface Callback {
        public boolean handleMessage(Message msg);        // 返回值意思就是callback處理完以後是否還須要繼續處理,true就是已經處理過了,不須要再進一步處理
    }

    另外值得注意的是Handler本身還有一個成員函數

    public void handleMessage(Message msg) {
    }

    擦,你這是要鬧哪樣!!

    查看下代碼的註釋:

    Callback:」Callback interface you can use when instantiating a Handler to avoid having to implement your own subclass of Handler.「

    handleMessage成員函數:」Subclasses must implement this to receive messages.「

    這樣就水落石出了:成員函數主要用在用戶本身繼承Handler實現時來處理message的,而Callback接口則是爲了用戶直接建立Handler時可以處理message用的,而沒必要經過子類實現。


(3)dispatchMessage

    Message中有一個Runnable的Callback成員,如今Handler中也有一個Callback,另外Handler還有一個成員函數handleMessage(Message),哇塞,暈掉了有木有,這三個必需要有一個執行次序。代碼以下:

public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            msg.callback.run();
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
}

即優先級爲:1. 消息體中的Runnable; 2. 若是爲空則執行建立handler時指定的Callback;3. 若是沒指定或者返回值爲false的話就執行handler的handleMessage方法。

至此,android中線程的消息循環和消息隊列就說明的差很少了。

Handler的Send/Post Mesage

在SDK文檔中的描述Handler,其有兩大主要用處:其一,安排message和runnable在某一時間點執行;其二,向其餘線程的消息隊列中發送消息(好比UI主線程)。

其中第二項功能就是咱們一般用來在異步線程和UI線程打交道的方法了。這主要經過Handler提供的send和post系列方法完成。send對應message,post對應Runnable。

以下:

post(Runnable)

postAtTime(Runnable, long)

postDelayed(Runnable, long)

sendEmptyMessage(int)

sendMessage(Message)

sendMessageAtTime(Message, long)

sendMessageDelayed(Message, long)

每一個方法的具體含義請自行查看SDK文檔。這裏仍是要解釋下post的實現:

public final boolean post(Runnable r)
 {
       return  sendMessageDelayed(getPostMessage(r), 0);
 }
public final boolean sendMessage(Message msg)
{
        return sendMessageDelayed(msg, 0);
}
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        if (queue == null) {
            // exception
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
 }


原來post也是將Runnable轉換成一個message(還記得Message有一個Runnable成員嗎),而後調用send的delay方法。最終的sendMessage也只是將消息壓入消息隊列中。

注意:這裏面的post和send含義和Windows應用程序的post和send的含義是不一樣的。

(1)線程內

    上面的分析,咱們就能夠在線程內向handler發送自定義message或post一個runnable。

(2)線程間

    以前咱們說過:一個Handler只能和建立它的線程綁定在一塊兒,這樣咱們就能夠在主線程建立一個Handler,而後再工做線程中向這個Handler 進行send 或post操做,這樣建立的message均會加入到UI線程的消息隊列中並被執行,這也就實現了UI線程和普通工做線程的通訊。以下一個代碼示例:

class MyActivity{
	Handler handler;
	
	... ...
	void init(){
		handler = new Handler() {
			@Override
			public void handleMessage(Message msg) {
				//處理消息:將網絡獲取的數據在UI上表現出來
				... ...
			}
		};
	}
	
	void getDataFormNet(){
		Thread thread = new Thread(new Runnable() {
			@Override
			public void run() {
				//網絡操做
				... ...
				handler.sendMessage(new Message());
			}
		});
		
		thread.start();
	}
}


OVER了,學習時間不長,若有錯誤請指正~~

相關文章
相關標籤/搜索