Android中Handler消息機制淺談

Message:消息;其中包含了消息ID,消息對象以及處理的數據等,由MessageQueue統一列隊,終由Handler處理

Handler:處理者;負責Message發送消息及處理。Handler通過與Looper進行溝通,從而使用Handler時,需要實現handlerMessage(Message msg)方法來對特定的Message進行處理,例如更新UI等(主線程中才行)

MessageQueue:消息隊列;用來存放Handler發送過來的消息,並按照FIFO(先入先出隊列)規則執行。當然,存放Message並非實際意義的保存,而是將Message以鏈表的方式串聯起來的,等Looper的抽取。

Looper:消息泵,不斷從MessageQueue中抽取Message執行。因此,一個線程中的MessageQueue需要一個Looper進行管理。Looper是當前線程創建的時候產生的(UI Thread即主線程是系統幫忙創建的Looper,而如果在子線程中,需要手動在創建線程後立即創建Looper[調用Looper.prepare()方法])。也就是說,會在當前線程上綁定一個Looper對象。

Thread:線程;負責調度消息循環,即消息循環的執行場所。

知識要點 

一、說明

1、handler應該由處理消息的線程創建。

2、handler與創建它的線程相關聯,而且也只與創建它的線程相關聯。handler運行在創建它的線程中,所以,如果在handler中進行耗時的操作,會阻塞創建它的線程。

二、一些知識點

1、Android的線程分爲有消息循環的線程和沒有消息循環的線程,有消息循環的線程一般都會有一個Looper。主線程(UI線程)就是一個消息循環的線程。

2、獲取looper

Looper.myLooper();     //獲得當前的Looper

Looper.getMainLooper() //獲得UI線程的Lopper

3、Handle的初始化函數(構造函數),如果沒有參數,那麼他就默認使用的是當前的Looper,如果有Looper參數,就是用對應的線程的Looper。

4、如果一個線程中調用Looper.prepare(),那麼系統就會自動的爲該線程建立一個消息隊列,然後調用 Looper.loop();之後就進入了消息循環,這個之後就可以發消息、取消息、和處理消息。

消息處理機制原理:

在創建Activity之前,當系統啓動的時候,先加載ActivityThread這個類,在這個類中的main函數,調用了Looper.prepareMainLooper();方法進行初始化Looper對象;然後創建了主線程的handler對象(Tips:加載ActivityThread的時候,其內部的Handler對象[靜態的]還未創建);隨後才創建了ActivityThread對象;最後調用了Looper.loop();方法,不斷的進行輪詢消息隊列的消息。也就是說,在ActivityThread和Activity創建之前(同樣也是Handler創建之前,當然handler由於這兩者初始化),就已經開啓了Looper的loop()方法,不斷的進行輪詢消息。需要注意的是,這個輪詢的方法是阻塞式的,沒有消息就一直等待(實際是等着MessageQueue的next()方法返回消息)。在應用一執行的時候,就已經開啓了Looper,並初始化了Handler對象。此時,系統的某些組件或者其他的一些活動等發送了系統級別的消息,這個時候主線程中的Looper就可以進行輪詢消息,並調用msg.target.dispatchMessage(msg)(msg.target即爲handler)進行分發消息,並通過handler的handleMessage方法進行處理;所以會優於我們自己創建的handler中的消息而處理系統消息。

 

0、準備數據和對象:

①、如果在主線程中處理message(即創建handler對象),那麼如上所述,系統的Looper已經準備好了(當然,MessageQueue也初始化了),且其輪詢方法loop已經開啓。【系統的Handler準備好了,是用於處理系統的消息】。【Tips:如果是子線程中創建handler,就需要顯式的調用Looper的方法prepare()和loop(),初始化Looper和開啓輪詢器】

②、通過Message.obtain()準備消息數據(實際是從消息池中取出的消息)

③、創建Handler對象,在其構造函數中,獲取到Looper對象、MessageQueue對象(從Looper中獲取的),並將handler作爲message的標籤設置到msg.target上

1、發送消息:sendMessage():通過Handler將消息發送給消息隊列

2、給Message貼上handler的標籤:在發送消息的時候,爲handler發送的message貼上當前handler的標籤

3、開啓HandlerThread線程,執行run方法。

4、在HandlerThread類的run方法中開啓輪詢器進行輪詢:調用Looper.loop()方法進行輪詢消息隊列的消息

Tips:這兩步需要再斟酌,個人認爲這個類是自己手動創建的一個線程類,Looper的開啓在上面已經詳細說明了,這裏是說自己手動創建線程(HandlerThread)的時候,纔會在這個線程中進行Looper的輪詢的】

5、在消息隊列MessageQueue中enqueueMessage(Message msg, long when)方法裏,對消息進行入列,即依據傳入的時間進行消息入列(排隊)

6、輪詢消息:與此同時,Looper在不斷的輪詢消息隊列

7、在Looper.loop()方法中,獲取到MessageQueue對象後,從中取出消息(Message msg = queue.next()

8、分發消息:從消息隊列中取出消息後,調用msg.target.dispatchMessage(msg);進行分發消息

9、將處理好的消息分發給指定的handler處理,即調用了handler的dispatchMessage(msg)方法進行分發消息。

10、在創建handler時,複寫的handleMessage方法中進行消息的處理

11、回收消息:在消息使用完畢後,在Looper.loop()方法中調用msg.recycle(),將消息進行回收,即將消息的所有字段恢復爲初始狀態。


二、詳細解釋:

1、準備Looper對象

兩種情況初始化Looper對象:

1)在主線程中不需要顯式的創建Looper對象,直接創建Handler對象即可;因爲在主線程ActivityThread的main函數中已經自動調用了創建Looper的方法:Looper.prepareMainLooper();,並在最後調用了Looper.loop()方法進行輪詢。

2)如果在子線程中創建Handler對象,需要創建Looper對象,即調用顯式的調用Looper.prepare()

初始化Looper的工作:

1)初始化Looper對象:通過調用Looper.prepare()初始化Looper對象,在這個方法中,新創建了Looper對象;在創建Looper的同時,(在其構造函數中)也初始化了MessageQueue。

2)將Looper綁定到當前線程:在初始化中,調用sThreadLocal.set(new Looper(quitAllowed))方法,將其和ThreadLocal進行綁定

在ThreadLocal對象中的set方法,是將當前線程和Looper綁定到一起:首先獲取到當前的線程,並獲取線程內部類Values,通過Thread.Values的put方法,將當前線程和Looper對象進行綁定到一起。即將傳入的Looper對象掛載到當前線程上。

Tips:在Looper對象中,可以通過getThread()方法,獲取到當前線程,即此Looper綁定的線程對象。

源代碼:

Looper中:

[java] view plain copy
 print?
  1. public static void prepare() {  
  2.         prepare(true);  
  3.     }  
  4.     private static void prepare(boolean quitAllowed) {  
  5.         if (sThreadLocal.get() != null) {  
  6.             throw new RuntimeException("Only one Looper may be created per thread");  
  7.         }  
  8.         sThreadLocal.set(new Looper(quitAllowed));  
  9.     }  
  10. private Looper(boolean quitAllowed) {  
  11.         mQueue = new MessageQueue(quitAllowed);  
  12.         mRun = true;  
  13.         mThread = Thread.currentThread();  
  14.     }  

ThreadLocal中:

[java] view plain copy
 print?
  1. public void set(T value) {  
  2.         Thread currentThread = Thread.currentThread();  
  3.         Values values = values(currentThread);  
  4.         if (values == null) {  
  5.             values = initializeValues(currentThread);  
  6.         }  
  7.         values.put(this, value);  
  8.     }  

2、創建消息Message:

消息的創建可以通過兩種方式:

1)new Message()

2)Message.obtain():【當存在多個handler的時候,可以通過Message.obtain(Handler handler)創建消息,指定處理的handler對象】

Tips:建議使用第二種方式更好一些。原因:

       因爲通過第一種方式,每有一個新消息,都要進行new一個Message對象,這會創建出多個Message,很佔內存。

       而如果通過obtain的方法,是從消息池sPool中取出消息。每次調用obtain()方法的時候,先判斷消息池是否有消息(if (sPool != null)),沒有則創建新消息對象,有則從消息池中取出消息,並將取出的消息從池中移除【具體看obtain()方法】

[java] view plain copy
 print?
  1. public static Message obtain() {  
  2.         synchronized (sPoolSync) {  
  3.             if (sPool != null) {  
  4.                 Message m = sPool;  
  5.                 sPool = m.next;  
  6.                 m.next = null;  
  7.                 sPoolSize--;  
  8.                 return m;  
  9.             }  
  10.         }  
  11.         return new Message();  
  12.     }  
  13.   
  14. public Message() {  
  15.     }  

3、創建Handler對象

兩種形式創建Handler對象:

1)創建無參構造函數的Handler對象:

2)創建指定Looper對象的Handler對象

最終都會調用相應的含有Callback和boolean類型的參數的構造函數

【這裏的Callback是控制是否分發消息的,其中含有一個返回值爲boolean的handleMessage(Message msg)方法進行判斷的;

 boolean類型的是參數是判斷是否進行異步處理,這個參數默認是系統處理的,我們無需關心】

在這個構造函數中,進行了一系列的初始化工作:

①、獲取到當前線程中的Looper對象

②、通過Looper對象,獲取到消息隊列MessageQueue對象

③、獲取Callback回調對象

④、獲取異步處理的標記

源代碼:

①、創建無參構造函數的Handler對象:

[java] view plain copy
 print?
  1. public Handler() {  
  2.         this(nullfalse);  
  3.     }  
  4. public Handler(Callback callback, boolean async) {  
  5.         if (FIND_POTENTIAL_LEAKS) {  
  6.             final Class<? extends Handler> klass = getClass();  
  7.             if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) && (klass.getModifiers() & Modifier.STATIC) == 0) {  
  8.                 Log.w(TAG, "The following Handler class should be static or leaks might occur: " +klass.getCanonicalName());  
  9.             }  
  10.         }  
  11.         mLooper = Looper.myLooper();  
  12.         if (mLooper == null) {  
  13.             throw new RuntimeException("Can't create handler inside thread that has not called Looper.prepare()");  
  14.         }  
  15.         mQueue = mLooper.mQueue;  
  16.         mCallback = callback;  
  17.         mAsynchronous = async;  
  18.     }  

②、創建指定Looper對象的Handler對象

[java] view plain copy
 print?
  1. public Handler(Looper looper) {  
  2.         this(looper, nullfalse);  
  3.     }  
  4. public Handler(Looper looper, Callback callback, boolean async) {  
  5.         mLooper = looper;  
  6.         mQueue = looper.mQueue;  
  7.         mCallback = callback;  
  8.         mAsynchronous = async;  
  9.     }  

4、Handler對象發送消息:

1)Handler發送消息給消息隊列:

Handler對象通過調用sendMessage(Message msg)方法,最終將消息發送給消息隊列進行處理

這個方法(所有重載的sendMessage)最終調用的是enqueueMessage(MessageQueuequeue, Message msg, long uptimeMillis)

(1)先拿到消息隊列:在調用到sendMessageAtTime(Messagemsg, long uptimeMillis)方法的時候,獲取到消息隊列(在創建Handler對象時獲取到的)

(2)當消息隊列不爲null的時候(爲空直接返回false,告知調用者處理消息失敗),再調用處理消息入列的方法:

enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)

這個方法,做了三件事:

①、爲消息打上標籤:msg.target = this;:將當前的handler對象這個標籤貼到傳入的message對象上,爲Message指定處理者

②、異步處理消息:msg.setAsynchronous(true);,在asyn爲true的時候設置

③、將消息傳遞給消息隊列MessageQueue進行處理:queue.enqueueMessage(msg, uptimeMillis);

[java] view plain copy
 print?
  1. public final boolean sendMessage(Message msg){  
  2.         return sendMessageDelayed(msg, 0);  
  3.     }  
  4. public final boolean sendMessageDelayed(Message msg, long delayMillis){  
  5.         if (delayMillis < 0) {  
  6.             delayMillis = 0;  
  7.         }  
  8.         return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);  
  9.     }  
  10. public boolean sendMessageAtTime(Message msg, long uptimeMillis) {  
  11.         MessageQueue queue = mQueue;  
  12.         if (queue == null) {  
  13.             RuntimeException e = new RuntimeException(  
  14.                     this + " sendMessageAtTime() called with no mQueue");  
  15.             Log.w("Looper", e.getMessage(), e);  
  16.             return false;  
  17.         }  
  18.         return enqueueMessage(queue, msg, uptimeMillis);  
  19.     }  
  20. private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {  
  21.         msg.target = this;  
  22.         if (mAsynchronous) {  
  23.             msg.setAsynchronous(true);  
  24.         }  
  25.         return queue.enqueueMessage(msg, uptimeMillis);  
  26.     }  

2)MessageQueue消息隊列處理消息:

在其中的enqueueMessage(Messagemsg, long when)方法中,工作如下:

在消息未被處理且handler對象不爲null的時候,進行如下操作(同步代碼塊中執行)

①、將傳入的處理消息的時間when(即爲上面的uptimeMillis)賦值爲當前消息的when屬性。

②、將next()方法中處理好的消息賦值給新的消息引用:Message p =mMessages;

       在next()方法中:不斷的從消息池中取出消息,賦值給mMessage,當沒有消息發來的時候,Looper的loop()方法由於是阻塞式的,就一直等消息傳進來

③、當傳入的時間爲0,且next()方法中取出的消息爲null的時候,將傳入的消息msg入列,排列在消息隊列上,此時爲消息是先進先出的

       否則,進入到死循環中,不斷的將消息入列,根據消息的時刻(when)來排列發送過來的消息,此時消息是按時間的先後進行排列在消息隊列上的

[java] view plain copy
 print?
  1. final boolean enqueueMessage(Message msg, long when) {  
  2.         if (msg.isInUse()) {  
  3.             throw new AndroidRuntimeException(msg + " This message is already in use.");  
  4.         }  
  5.         if (msg.target == null) {  
  6.             throw new AndroidRuntimeException("Message must have a target.");  
  7.         }  
  8.         boolean needWake;  
  9.         synchronized (this) {  
  10.             if (mQuiting) {  
  11.                 RuntimeException e = new RuntimeException(msg.target + " sending message to a Handler on a dead thread");  
  12.                 Log.w("MessageQueue", e.getMessage(), e);  
  13.                 return false;  
  14.             }  
  15.   
  16.             msg.when = when;  
  17.             Message p = mMessages;  
  18.             if (p == null || when == 0 || when < p.when) {  
  19.                 // New head, wake up the event queue if blocked.  
  20.                 msg.next = p;  
  21.                 mMessages = msg;  
  22.                 needWake = mBlocked;  
  23.             } else {  
  24.                 needWake = mBlocked && p.target == null && msg.isAsynchronous();  
  25.                 Message prev;  
  26.                 for (;;) {  
  27.                     prev = p;  
  28.                     p = p.next;  
  29.                     if (p == null || when < p.when) {  
  30.                         break;  
  31.                     }  
  32.                     if (needWake && p.isAsynchronous()) {  
  33.                         needWake = false;  
  34.                     }  
  35.                 }  
  36.                 msg.next = p; // invariant: p == prev.next  
  37.                 prev.next = msg;  
  38.             }  
  39.         }  
  40.         if (needWake) {  
  41.             nativeWake(mPtr);  
  42.         }  
  43.         return true;  
  44.     }  

5、輪詢Message

1)開啓loop輪詢消息

當開啓線程的時候,執行run方法,在HandlerThread類中,調用的run方法中將開啓loop進行輪詢消息隊列:

在loop方法中,先拿到MessageQueue對象,然後死循環不斷從隊列中取出消息,當消息不爲null的時候,通過handler分發消息:msg.target.dispatchMessage(msg)。消息分發完之後,調用msg.recycle()回收消息,

2)回收消息:

在Message的回收消息recycle()這個方法中:首先調用clearForRecycle()方法,將消息的所有字段都恢復到原始狀態【如flags=0,what=0,obj=null,when=0等等】

然後在同步代碼塊中將消息放回到消息池sPool中,重新利用Message對象

源代碼:

Looper.loop()

[java] view plain copy
 print?
  1. public static void loop() {  
  2.         final Looper me = myLooper();  
  3.         if (me == null) {  
  4.             throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");  
  5.         }  
  6.         final MessageQueue queue = me.mQueue;  
  7.         Binder.clearCallingIdentity();  
  8.         final long ident = Binder.clearCallingIdentity();  
  9.         for (;;) {  
  10.             Message msg = queue.next(); // might block  
  11.             if (msg == null) {  
  12.                return;  
  13.             }  
  14.             // This must be in a local variable, in case a UI event sets the logger  
  15.             Printer logging = me.mLogging;  
  16.             if (logging != null) {  
  17.                 logging.println(">>>>> Dispatching to " + msg.target + " " +  
  18.                         msg.callback + ": " + msg.what);  
  19.             }  
  20.             msg.target.dispatchMessage(msg);  
  21.             if (logging != null) {  
  22.                 logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);  
  23.             }  
  24.             final long newIdent = Binder.clearCallingIdentity();  
  25.             if (ident != newIdent) {  
  26.                 Log.wtf(TAG, “……”);  
  27.             }  
  28.             msg.recycle();  
  29.         }  
  30.     }  

msg.recycle();:

[java] view plain copy
 print?
  1. public void recycle() {  
  2.         clearForRecycle();  
  3.         synchronized (sPoolSync) {  
  4.             if (sPoolSize < MAX_POOL_SIZE) {  
  5.                 next = sPool;  
  6.                 sPool = this;  
  7.                 sPoolSize++;  
  8.             }  
  9.         }  
  10.     }  

[java] view plain copy
 print?
  1. /*package*/ void clearForRecycle() {  
  2.         flags = 0;  
  3.         what = 0;  
  4.         arg1 = 0;  
  5.         arg2 = 0;  
  6.         obj = null;  
  7.         replyTo = null;  
  8.         when = 0;  
  9.         target = null;  
  10.         callback = null;  
  11.         data = null;  
  12.     }  

6、處理Message

在Looper.loop()方法中調用了msg.target.dispatchMessage(msg);的方法,就是調用了Handler中的dispatchMessage(Message msg)方法:

1)依據Callback中的handleMessage(msg)的真假判斷是否要處理消息,如果是真則不進行消息分發,則不處理消息,否則進行處理消息

2)當Callback爲null或其handleMessage(msg)的返回值爲false的時候,進行分發消息,即調用handleMessage(msg)處理消息【這個方法需要自己複寫】

[java] view plain copy
 print?
  1. /** 
  2.      * Subclasses must implement this to receive messages. 
  3.      */  
  4.     public void handleMessage(Message msg) {  
  5.     }  
  6.       
  7.     /** 
  8.      * Handle system messages here. 
  9.      */  
  10.     public void dispatchMessage(Message msg) {  
  11.         if (msg.callback != null) {  
  12.             handleCallback(msg);  
  13.         } else {  
  14.             if (mCallback != null) {  
  15.                 if (mCallback.handleMessage(msg)) {  
  16.                     return;  
  17.                 }  
  18.             }  
  19.             handleMessage(msg);  
  20.         }  
  21.     }  
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章