1. 程式人生 > >Android訊息處理機制——Looper、Handler、Message 原始碼分析

Android訊息處理機制——Looper、Handler、Message 原始碼分析

原文地址:http://blog.csdn.net/wzy_1988/article/details/38346637

前言

    雖然一直在做應用層開發,但是我們組是核心系統BSP,瞭解底層瞭解Android的執行機制還是很有必要的。就應用程式而言,Android系統中的Java應用程式和其他系統上相同,都是靠訊息驅動來工作的,它們大致的工作原理如下:
  1. 有一個訊息佇列,可以往這個訊息佇列中投遞訊息。
  2. 有一個訊息迴圈,不斷從訊息佇列中取出訊息,然後處理 。
    為了更深入的理解Android的訊息處理機制,這幾天空閒時間,我結合《深入理解Android系統》看了Handler、Looper、Message這幾個類的原始碼,這裡分享一下學習心得。

Looper類分析

    在分析之前,我先把Looper類的原始碼show出來,非常精簡的程式碼,原始碼如下(frameworks/base/core/java/android/os/Looper.java): [java] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. publicfinalclass Looper {  
  2.     privatestaticfinal String TAG = "Looper";  
  3.     // sThreadLocal.get() will return null unless you've called prepare().
  4.     staticfinal ThreadLocal<Looper> sThreadLocal = 
    new ThreadLocal<Looper>();  
  5.     privatestatic Looper sMainLooper;  // guarded by Looper.class
  6.     final MessageQueue mQueue;  
  7.     final Thread mThread;  
  8.     private Printer mLogging;  
  9.      /** Initialize the current thread as a looper. 
  10.       * This gives you a chance to create handlers that then reference
     
  11.       * this looper, before actually starting the loop. Be sure to call 
  12.       * {@link #loop()} after calling this method, and end it by calling 
  13.       * {@link #quit()}. 
  14.       */
  15.     publicstaticvoid prepare() {  
  16.         prepare(true);  
  17.     }  
  18.     privatestaticvoid prepare(boolean quitAllowed) {  
  19.         if (sThreadLocal.get() != null) {  
  20.             thrownew RuntimeException("Only one Looper may be created per thread");  
  21.         }  
  22.         sThreadLocal.set(new Looper(quitAllowed));  
  23.     }  
  24.     /** 
  25.      * Initialize the current thread as a looper, marking it as an 
  26.      * application's main looper. The main looper for your application 
  27.      * is created by the Android environment, so you should never need 
  28.      * to call this function yourself.  See also: {@link #prepare()} 
  29.      */
  30.     publicstaticvoid prepareMainLooper() {  
  31.         prepare(false);  
  32.         synchronized (Looper.class) {  
  33.             if (sMainLooper != null) {  
  34.                 thrownew IllegalStateException("The main Looper has already been prepared.");  
  35.             }  
  36.             sMainLooper = myLooper();  
  37.         }  
  38.     }  
  39.     /** Returns the application's main looper, which lives in the main thread of the application. 
  40.      */
  41.     publicstatic Looper getMainLooper() {  
  42.         synchronized (Looper.class) {  
  43.             return sMainLooper;  
  44.         }  
  45.     }  
  46.     /** 
  47.      * Run the message queue in this thread. Be sure to call 
  48.      * {@link #quit()} to end the loop. 
  49.      */
  50.     publicstaticvoid loop() {  
  51.         final Looper me = myLooper();  
  52.         if (me == null) {  
  53.             thrownew RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");  
  54.         }  
  55.         final MessageQueue queue = me.mQueue;  
  56.         // Make sure the identity of this thread is that of the local process,
  57.         // and keep track of what that identity token actually is.
  58.         Binder.clearCallingIdentity();  
  59.         finallong ident = Binder.clearCallingIdentity();  
  60.         for (;;) {  
  61.             Message msg = queue.next(); // might block
  62.             if (msg == null) {  
  63.                 // No message indicates that the message queue is quitting.
  64.                 return;  
  65.             }  
  66.             // This must be in a local variable, in case a UI event sets the logger
  67.             Printer logging = me.mLogging;  
  68.             if (logging != null) {  
  69.                 logging.println(">>>>> Dispatching to " + msg.target + " " +  
  70.                         msg.callback + ": " + msg.what);  
  71.             }  
  72.             msg.target.dispatchMessage(msg);  
  73.             if (logging != null) {  
  74.                 logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);  
  75.             }  
  76.             // Make sure that during the course of dispatching the
  77.             // identity of the thread wasn't corrupted.
  78.             finallong newIdent = Binder.clearCallingIdentity();  
  79.             if (ident != newIdent) {  
  80.                 Log.wtf(TAG, "Thread identity changed from 0x"
  81.                         + Long.toHexString(ident) + " to 0x"
  82.                         + Long.toHexString(newIdent) + " while dispatching to "
  83.                         + msg.target.getClass().getName() + " "
  84.                         + msg.callback + " what=" + msg.what);  
  85.             }  
  86.             msg.recycle();  
  87.         }  
  88.     }  
  89.     /** 
  90.      * Return the Looper object associated with the current thread.  Returns 
  91.      * null if the calling thread is not associated with a Looper. 
  92.      */
  93.     publicstatic Looper myLooper() {  
  94.         return sThreadLocal.get();  
  95.     }  
  96.     publicvoid setMessageLogging(Printer printer) {  
  97.         mLogging = printer;  
  98.     }  
  99.     /** 
  100.      * Return the {@link MessageQueue} object associated with the current 
  101.      * thread.  This must be called from a thread running a Looper, or a 
  102.      * NullPointerException will be thrown. 
  103.      */
  104.     publicstatic MessageQueue myQueue() {  
  105.         return myLooper().mQueue;  
  106.     }  
  107.     private Looper(boolean quitAllowed) {  
  108.         mQueue = new MessageQueue(quitAllowed);  
  109.         mThread = Thread.currentThread();  
  110.     }  
  111.     /** 
  112.      * Returns true if the current thread is this looper's thread. 
  113.      * @hide 
  114.      */
  115.     publicboolean isCurrentThread() {  
  116.         return Thread.currentThread() == mThread;  
  117.     }  
  118.     publicvoid quit() {  
  119.         mQueue.quit(false);  
  120.     }  
  121.     publicvoid quitSafely() {  
  122.         mQueue.quit(true);  
  123.     }  
  124.     publicint postSyncBarrier() {  
  125.         return mQueue.enqueueSyncBarrier(SystemClock.uptimeMillis());  
  126.     }  
  127.     publicvoid removeSyncBarrier(int token) {  
  128.         mQueue.removeSyncBarrier(token);  
  129.     }  
  130.     /** 
  131.      * Return the Thread associated with this Looper. 
  132.      */
  133.     public Thread getThread() {  
  134.         return mThread;  
  135.     }  
  136.     /** @hide */
  137.     public MessageQueue getQueue() {  
  138.         return mQueue;  
  139.     }  
  140.     /** 
  141.      * Return whether this looper's thread is currently idle, waiting for new work 
  142.      * to do.  This is intrinsically racy, since its state can change before you get 
  143.      * the result back. 
  144.      * @hide 
  145.      */
  146.     publicboolean isIdling() {  
  147.         return mQueue.isIdling();  
  148.     }  
  149.     publicvoid dump(Printer pw, String prefix) {  
  150.         pw.println(prefix + toString());  
  151.         mQueue.dump(pw, prefix + "  ");  
  152.     }  
  153.     public String toString() {  
  154.         return"Looper (" + mThread.getName() + ", tid " + mThread.getId()  
  155.                 + ") {" + Integer.toHexString(System.identityHashCode(this)) + "}";  
  156.     }  
  157. }  
    Looper字面意思是“迴圈”,它被設計用來將一個普通的Thread執行緒變成Looper Thread執行緒。所謂Looper執行緒就是迴圈工作的執行緒,在程式開發(尤其是GUI開發)中,我們經常會使用到一個迴圈執行的執行緒,有新任務就立刻執行,沒有新任務就迴圈等待。使用Looper建立Looper Thread很簡單,示例程式碼如下: [java] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. package com.example.testlibrary;  
  2. import android.os.Handler;  
  3. import android.os.Looper;  
  4. publicclass LooperTheread extends Thread{  
  5.     public Handler mhHandler;  
  6.     @Override
  7.     publicvoid run() {  
  8.         // 1. 呼叫Looper
  9.         Looper.prepare();  
  10.         // ... 其他處理,例如例項化handler
  11.         // 2. 進入訊息迴圈
  12.         Looper.loop();  
  13.     }  
  14. }  
    通過1、2兩步核心程式碼,你的執行緒就升級為Looper執行緒了。下面,我們對兩個關鍵呼叫1、2進行逐一分析。

Looper.prepare()

    在呼叫prepare的執行緒中,new了一個Looper物件,並將這個Looper物件儲存在這個呼叫執行緒的ThreadLocal中。而Looper物件內部封裝了一個訊息佇列。
    我們來看一下Looper類的原始碼。第一個呼叫函式是Looper的prepare函式,它的原始碼如下: [java] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. // 每個執行緒中的Looper物件其實是一個ThreadLocal,即執行緒本地儲存(TLS)物件
  2. staticfinal ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();  
  3. publicstaticvoid prepare() {  
  4.     prepare(true);  
  5. }  
  6. privatestaticvoid prepare(boolean quitAllowed) {  
  7.     if (sThreadLocal.get() != null) {  
  8.         thrownew RuntimeException("Only one Looper may be created per thread");  
  9.     }  
  10.     sThreadLocal.set(new Looper(quitAllowed));  
  11. }  
    根據上面的原始碼可知,prepare會在呼叫執行緒的區域性變數中設定一個Looper物件,並且一個Thread只能有一個Looper物件。這個呼叫執行緒就是LooperThread的run執行緒。來看一下Looper物件的構造原始碼: [java] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. private Looper(boolean quitAllowed) {  
  2.     mQueue = new MessageQueue(quitAllowed);  
  3.     mThread = Thread.currentThread();  
  4. }  
通過原始碼,我們可以輕鬆瞭解Looper的工作方式,其核心就是將Looper物件儲存到當前執行緒的ThreadLocal中,並且保證該Looper物件只new一次。如果不理解ThreadLocal,可以參考我這篇文章:正確理解ThreadLocal

Looper迴圈

呼叫了Loop方法後,Looper執行緒就開始真正的工作了,它不斷從自己的MessageQueue中取出對頭的資訊(也叫任務)執行,如圖所示:
    其實現原始碼如下所示(這裡我做了一些修整,去掉不影響主線的程式碼): [java] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. /** 
  2.  * Run the message queue in this thread. Be sure to call 
  3.  * {@link #quit()} to end the loop. 
  4.  */
  5. publicstaticvoid loop() {  
  6.     final Looper me = myLooper();  
  7.     if (me == null) {  
  8.         thrownew RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");  
  9.     }  
  10.     // 取出這個Looper的訊息佇列
  11.     final MessageQueue queue = me.mQueue;  
  12.     for (;;) {  
  13.         Message msg = queue.next(); // might block
  14.         if (msg == null) {  
  15.             // No message indicates that the message queue is quitting.
  16.             return;  
  17.         }  
  18.         // 處理訊息,Message物件中有一個target,它是Handler型別
  19.         msg.target.dispatchMessage(msg);  
  20.         msg.recycle();  
  21.     }  
  22. }  
  23. /** 
  24.  * Return the Looper object associated with the current thread.  Returns 
  25.  * null if the calling thread is not associated with a Looper. 
  26.  */
  27. publicstatic Looper myLooper() {  
  28.     return sThreadLocal.get();  
  29. }  
    通過上面的分析會發現,Looper的作用是:
  1. 封裝了一個訊息佇列。
  2. Looper的prepare函式把這個Looper和呼叫prepare的執行緒(也就是最終處理的執行緒)繫結在一起,通過ThreadLocal機制實現的。
  3. 處理執行緒呼叫loop函式,處理來自該訊息佇列的訊息。
    如何往MessageQueue裡新增訊息,是由Handler實現的,下面來分析一下Handler。

Handler分析

    什麼是handler?handler扮演了往MessageQueue裡新增訊息和處理訊息的角色(只處理由自己發出的訊息),即通過MessageQueue它要執行一個任務(sendMessage),並在loop到自己的時候執行該任務(handleMessage),整個過程是非同步的。

初識Handler

    Handler中的所包括的成員變數: [java] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. final MessageQueue mQueue;  // Handler中也有一個訊息佇列
  2. final Looper mLooper;   // 也有一個Looper
  3. final Callback mCallback;   // 有一個回撥類
    這幾個成員變數的使用,需要分析Handler的建構函式。Handler有N多建構函式,但是我們只分析最簡單的情況,在當前執行緒中直接new一個Handler(Handler handler = new Handler())。我們看一下建構函式是如何完成初始化操作的(frameworks/base/core/java/android/os/Handler.java): [java] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. public Handler() {  
  2.     this(nullfalse);  
  3. }  
  4. /** 
  5.  * Use the {@link Looper} for the current thread with the specified callback interface 
  6.  * and set whether the handler should be asynchronous. 
  7.  * 
  8.  * Handlers are synchronous by default unless this constructor is used to make 
  9.  * one that is strictly asynchronous. 
  10.  * 
  11.  * Asynchronous messages represent interrupts or events that do not require global ordering 
  12.  * with represent to synchronous messages.  Asynchronous messages are not subject to 
  13.  * the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}. 
  14.  * 
  15.  * @param callback The callback interface in which to handle messages, or null. 
  16.  * @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for 
  17.  * each {@link Message} that is sent to it or {@link Runnable} that is posted to it. 
  18.  * 
  19.  * @hide 
  20.  */
  21. public Handler(Callback callback, boolean async) {  
  22.     if (FIND_POTENTIAL_LEAKS) {  
  23.         final Class<? extends Handler> klass = getClass();  
  24.         if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&  
  25.                 (klass.getModifiers() & Modifier.STATIC) == 0) {  
  26.             Log.w(TAG, "The following Handler class should be static or leaks might occur: " +  
  27.                 klass.getCanonicalName());  
  28.         }  
  29.     }  
  30.     mLooper = Looper.myLooper();  
  31.     if (mLooper == null) {  
  32.         thrownew RuntimeException(  
  33.             "Can't create handler inside thread that has not called Looper.prepare()");  
  34.     }  
  35.     mQueue = mLooper.mQueue;  
  36.     mCallback = callback;  
  37.     mAsynchronous = async;  
  38. }  
    通過上面的建構函式,我們可以發現,當前Handler中的mLooper是從Looper.myLooper()函式獲取來的,而這個函式的定義我再複製一下,如下所示: [java] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. /** 
  2.  * Return the Looper object associated with the current thread.  Returns 
  3.  * null if the calling thread is not associated with a Looper. 
  4.  */
  5. publicstatic Looper myLooper() {  
  6.     return sThreadLocal.get();  
  7. }  
    google原始碼的註釋也是很清楚的。可以看到,Handler中的Looper物件是Handler物件所屬執行緒的Looper物件。如果Handler是在UI執行緒中例項化的,那Looper物件就是UI執行緒的物件。如果Handler是在子執行緒中例項化的,那Looper物件就是子執行緒的Looper物件(基於ThreadLocal機制實現)。

Handler真面目

    由上面分析可知,Handler中的訊息佇列實際上就是Handler所屬執行緒的Looper物件的訊息佇列,我們可以為之前的LooperThread類增加Handler,程式碼如下: [java] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. publicclass LooperThread extends Thread{  
  2.     public Handler mhHandler;  
  3.     @Override
  4.     publicvoid run() {  
  5.         // 1. 呼叫Looper
  6.         Looper.prepare();  
  7.         // ... 其他處理,例如例項化handler
  8.         Handler handler = new Handler();  
  9.         // 2. 進入訊息迴圈
  10.         Looper.loop();  
  11.     }  
  12. }  
    加入Handler的效果圖如下所示:     問一個問題,假設沒有Handler,我們該如何往Looper的MessageQueue裡插入訊息呢?這裡我說一個原始的思路:
  1. 呼叫Looper的myQueue,它將返回訊息佇列物件MessageQueue。
  2. 構造一個Message,填充它的成員,尤其是target物件。
  3. 呼叫MessageQueue的enqueueMessage,將訊息插入到訊息佇列中。
    上面的方法雖然能工作,但是非常原始,有了Handler以後,它像一個輔助類,提供了一系列API呼叫,幫我們簡化程式設計工作。常用API如下:
  1. post(Runnable)
  2. postAtTime(Runnable, long)
  3. postDelayed(Runnable, long)
  4. sendEmptyMessage(int)
  5. sendMessage(Message)
  6. sendMessageAtTime(Message, long)
  7. sendMessageDelayed(Message, long)
    光看以上的API,你會認為handler可能會發送兩種資訊,一種是Runnable物件,一種是Message物件,這是主觀的理解,但是從原始碼中我們可以看到,post發出的Runnable物件最後都被封裝成了Message物件,原始碼如下: [java] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. publicfinalboolean post(Runnable r)  
  2. {  
  3.    return  sendMessageDelayed(getPostMessage(r), 0);  
  4. }  
  5. privatestatic Message getPostMessage(Runnable r) {  
  6.     Message m = Message.obtain();   // 得到空的message
  7.     m.callback = r; // 將runnable設定為message的callback
  8.     return m;  
  9. }  
  10. publicfinalboolean sendMessageDelayed(Message msg, long delayMillis)  
  11. {  
  12.     if (delayMillis < 0) {  
  13.         delayMillis = 0;  
  14.     }  
  15.     return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);  
  16. }  
    最終傳送訊息都會呼叫sendMessageAtTime函式,我們看一下它的原始碼實現: [java] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. publicboolean sendMessageAtTime(Message msg, long uptimeMillis) {  
  2.     MessageQueue queue = mQueue;  
  3.     if (queue == null) {  
  4.         RuntimeException e = new RuntimeException(  
  5.                 this + " sendMessageAtTime() called with no mQueue");  
  6.         Log.w("Looper", e.getMessage(), e);  
  7.         returnfalse;  
  8.     }  
  9.     return enqueueMessage(queue, msg, uptimeMillis);  
  10. }  
  11. privateboolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {  
  12.     msg.target = this;  // 將Message的target設定為當前的Handler,然後將訊息自己加到訊息佇列中
  13.     if (mAsynchronous) {  
  14.         msg.setAsynchronous(true);  
  15.     }  
  16.     return queue.enqueueMessage(msg, uptimeMillis);  
  17. }  

Handler處理訊息

    講完了訊息傳送,再看一下Handler是如何處理訊息的。訊息的處理是通過核心方法dispatchMessage(Message msg)與鉤子方法handleMessage(Message msg)完成的,原始碼如下: [java] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. /** 
  2.  * Handle system messages here. 
  3.  */
  4. publicvoid dispatchMessage(Message msg) {  
  5.     if (msg.callback != null) {  
  6.         handleCallback(msg);  
  7.     } else {  
  8.         if (mCallback != null) {  
  9.             if (mCallback.handleMessage(msg)) {  
  10.                 return;  
  11.             }  
  12.         }  
  13.         handleMessage(msg);  
  14.     }  
  15. }  
  16. privatestaticvoid handleCallback(Message message) {  
  17.     message.callback.run();  
  18. }  
  19. /** 
  20.  * Subclasses must implement this to receive messages. 
  21.  */
  22. publicvoid handleMessage(Message msg) {  
  23. }  
    dispatchMessage定義了一套訊息處理的優先順序機制,它們分別是:
  1. 如果Message自帶了callback處理,則交給callback處理。例如上文分析的,Handler裡通過post(Runnable r)發生一個Runnable物件,則msg的callback物件就被賦值為Runnable物件。
  2. 如果Handler設定了全域性的mCallback,則交給mCallback處理。
  3. 如果上述都沒有,該訊息會被交給Handler子類實現的handlerMessage(Message msg)來處理。當然,這需要從Handler派生並重寫HandlerMessage函式。
    在通過情況下,我們一般都是採用第三種方法,即在子類中通過過載handlerMessage來完成處理工作。

Handler的用處

    看完了Handler的傳送訊息和處理訊息,我們來學習一下Handler被稱為非同步處理大師的真正牛逼之處。Hanlder有兩個重要的特點:     1. handler可以在任意執行緒上傳送訊息,這些訊息會被新增到Handler所屬執行緒的Looper物件的訊息佇列裡。
    2. handler是在例項化它的執行緒中處理訊息的。
    這解決了Android經典的不能在非主執行緒中更新UI的問題。Android的主執行緒也是一個Looper執行緒,我們在其中建立的Handler將預設關聯主執行緒Looper的訊息佇列。因此,我們可以在主執行緒建立Handler物件,在耗時的子執行緒獲取UI資訊後,通過主執行緒的Handler物件引用來發生訊息給主執行緒,通知修改UI,當然訊息了還可以包含具體的UI資料。

Message

    在整個訊息處理機制中,Message又叫做task,封裝了任務攜帶的訊息和處理該任務的handler。Message的原始碼比較簡單,原始碼位置(frameworks/base/core/java/android/os/Message.java)這裡簡單說明幾點注意事項:     1. 儘管Message有public的預設構造方法,但是你應該通過Message.obtain()來從訊息池中獲得空訊息物件,以節省資源,原始碼如下: [java] view plain copy print?在CODE上檢視程式碼片派生到我的程式碼片
  1. /** 
  2.  * Return a new Message instance from the global pool. Allows us to 
  3.  * avoid allocating new objects in many cases. 
  4.  */
  5. publicstatic Message obtain() {  
  6.     synchronized (sPoolSync) {  
  7.         if (sPool != null) {  
  8.             Message m = sPool;  
  9.             sPool = m.next;  
  10.             m.next = null;  
  11.             sPoolSize--;  
  12.             return m;  
  13.         }  
  14.     }