Android Handler源碼分析核心架構


前言:

  對於一個Android研發而言,親身體會就是不管在平時開發或者面試的時候,Handler消息機制毋庸置疑都是一個必備的知識點,所以這邊留一份個人筆記,如有分析不對的地方,還望指出!

目錄:

  1、如何分析Handler源碼

  2、源碼大致流程:消息的入隊與出隊

  3、從大致流程進入細化分析

    3.1、Handler、Looper、MessageQueue三者之間的關系

    3.2、Handler、Looper、MessageQueue之間的協作

總結圖1:Handler在子線程中發送消息,消息會被添加到MessageQueue消息隊列中,再來由Handler所處的當前線程的Looper來不斷的輪詢MessageQueue以獲取出隊消息,最后調用dispatchMessage進行消息傳遞給handleMessage進行處理

 

 

1、如何分析源碼

  眾所皆知的Android源碼的有很多,涉及到一個類或者多個類,一個類中又有很多代碼,所以這邊最簡單的分析方式就是回歸到Handler的使用中來,也就是如何使用Handler

  1.1、實例一個Handler對象(主線程)

  1.2、在子線程中使用Handler發送一個消息,如:handler.sendEmptyMessage(1)

  1.3、消息發送出之后(執行1.2步驟),消息最終會被轉發到我們new出來的Handler中的handleMessage方法進行處理(子線程消息發送到主線程中處理)

  以上3個步驟即為我們對Handler的基本使用方式,所以,我們可以以發送消息的時機,作為源碼分析的切入點,並留下一個疑問:

  問題1:子線程發送的消息為什么是在主線程中接收的呢?

 2、源碼大致流程:消息的入隊與出隊

  2.1、消息發送:sendMessage(Message msg) \ sendEmptyMessage(int what) \ postDelayed(Runnable r, long delayMillis) 等等

  2.2、消息及發送時間處理:sendMessageAtTime(Message msg, long uptimeMillis)

  2.3、消息隊列添加:enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)

  2.4、消息出隊:到這里,既然有消息添加到隊列中的流程,而且我們最終都會獲得相應的消息返回,那么消息是如何出隊的呢?帶着這個疑問,我們最終在MessageQueue 消息隊列中找到一個函數名稱為 next() 的函數。

  問題2:這個next()函數 是在什么時候調用的呢?

  在Handler源碼上,以消息發送作為分析切入點來查看,如2.1羅列的幾種消息發送方式,我們都可以很清楚的發現,消息都是調用了sendMessageDelayed(Message msg, long delayMillis),最終調用到sendMessageAtTime(Message msg, long uptimeMillis),然后在該方法里面調用了enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis),到這里,不管從方法名稱還是局部變量的名稱來看,這邊都出現了一個隊列的信息,所以可以知道Handler的消息發送最終是在sendMessageAtTime里面調用了MessageQueue.enqueueMessage()對消息進行隊列添加,然后調用了MessageQueue.next()進行消息輪詢並返回Message結果。

3、從大致流程進入細化分析

  3.1、Handler、Looper、MessageQueue三者之間的 關系圖2 如下:

  

  在分析到第2步的sendMessageAtTime結束時,我們這邊引出了一個消息隊列的內容:MessageQueue queue = mQueue

 1     /**
 2      * Enqueue a message at the front of the message queue, to be processed on
 3      * the next iteration of the message loop.  You will receive it in
 4      * {@link #handleMessage}, in the thread attached to this handler.
 5      * <b>This method is only for use in very special circumstances -- it
 6      * can easily starve the message queue, cause ordering problems, or have
 7      * other unexpected side-effects.</b>
 8      *  
 9      * @return Returns true if the message was successfully placed in to the 
10      *         message queue.  Returns false on failure, usually because the
11      *         looper processing the message queue is exiting.
12      */
13     public final boolean sendMessageAtFrontOfQueue(Message msg) {
14         MessageQueue queue = mQueue;
15         if (queue == null) {
16             RuntimeException e = new RuntimeException(
17                 this + " sendMessageAtTime() called with no mQueue");
18             Log.w("Looper", e.getMessage(), e);
19             return false;
20         }
21         return enqueueMessage(queue, msg, 0);
22     }
23 
24     private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
25         msg.target = this;
26         if (mAsynchronous) {
27             msg.setAsynchronous(true);
28         }
29         return queue.enqueueMessage(msg, uptimeMillis);
30     }

  問題:mQueue是什么東西?這個mQueue是怎么來的?所以我們在Handler的構造方法中找到了它的初始化位置

 1     /**
 2      * Use the {@link Looper} for the current thread with the specified callback interface
 3      * and set whether the handler should be asynchronous.
 4      *
 5      * Handlers are synchronous by default unless this constructor is used to make
 6      * one that is strictly asynchronous.
 7      *
 8      * Asynchronous messages represent interrupts or events that do not require global ordering
 9      * with respect to synchronous messages.  Asynchronous messages are not subject to
10      * the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.
11      *
12      * @param callback The callback interface in which to handle messages, or null.
13      * @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for
14      * each {@link Message} that is sent to it or {@link Runnable} that is posted to it.
15      *
16      * @hide
17      */
18     public Handler(Callback callback, boolean async) {
19         if (FIND_POTENTIAL_LEAKS) {
20             final Class<? extends Handler> klass = getClass();
21             if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
22                     (klass.getModifiers() & Modifier.STATIC) == 0) {
23                 Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
24                     klass.getCanonicalName());
25             }
26         }
27 
28         mLooper = Looper.myLooper();
29         if (mLooper == null) {
30             throw new RuntimeException(
31                 "Can't create handler inside thread that has not called Looper.prepare()");
32         }
33         mQueue = mLooper.mQueue;
34         mCallback = callback;
35         mAsynchronous = async;
36     }

  到此,從上面的兩段源碼,且帶着第2點中,next()被調用的時機問題,我們引出了os/Handler中的兩個成員變量

    final Looper mLooper;
    final MessageQueue mQueue;

   MessageQueue 對象是從Looper中獲得的,也就是說mQueue是在Looper中實例化的,所以很明顯,Handler中的消息隊列MessageQueue 是從輪詢器Looper中獲得的。

 

  那么問題來了:為什么消息隊列要在輪詢器中進行實例化,請看以下源碼

 1     /**
 2      * Return the {@link MessageQueue} object associated with the current
 3      * thread.  This must be called from a thread running a Looper, or a
 4      * NullPointerException will be thrown.
 5      */
 6     public static @NonNull MessageQueue myQueue() {
 7         return myLooper().mQueue;
 8     }
 9 
10     private Looper(boolean quitAllowed) {
11         mQueue = new MessageQueue(quitAllowed);
12         mThread = Thread.currentThread();
13     }

  MessageQueue在Looper中進行實例化,也就是說一個Looper就有一個MessageQueue,屬於綁定關系,從而得出一個Looper只能輪詢一個消息隊列

  所以可得出如關系圖2中Handler、Looper、MessageQueue三者的關系:Handler中持有Looper和MessageQueue,Looper中持有MessageQueue,而且Handler中的MessageQueue來自於Looper中的MessageQueue。

  Handler是使用時通過New實例化出來的,MessageQueue是在Looper中進行實例的,那么這個Looper是如何實例化的?所以這邊我們將引出 ActivityThread。而ActivityThread是什么東西呢?這邊就稍微介紹一下:

  安卓應用程序作為一個控制類程序,跟Java程序類似,都是有一個入口的,而這個入口就是ActivityThread的main函數:

 1 public static void main(String[] args) {
 2         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
 3         SamplingProfilerIntegration.start();
 4 
 5         // CloseGuard defaults to true and can be quite spammy.  We
 6         // disable it here, but selectively enable it later (via
 7         // StrictMode) on debug builds, but using DropBox, not logs.
 8         CloseGuard.setEnabled(false);
 9 
10         Environment.initForCurrentUser();
11 
12         // Set the reporter for event logging in libcore
13         EventLogger.setReporter(new EventLoggingReporter());
14 
15         // Make sure TrustedCertificateStore looks in the right place for CA certificates
16         final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
17         TrustedCertificateStore.setDefaultUserDirectory(configDir);
18 
19         Process.setArgV0("<pre-initialized>");
20 
21         Looper.prepareMainLooper();
22 
23         ActivityThread thread = new ActivityThread();
24         thread.attach(false);
25 
26         if (sMainThreadHandler == null) {
27             sMainThreadHandler = thread.getHandler();
28         }
29 
30         if (false) {
31             Looper.myLooper().setMessageLogging(new
32                     LogPrinter(Log.DEBUG, "ActivityThread"));
33         }
34 
35         // End of event ActivityThreadMain.
36         Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
37         Looper.loop();
38 
39         throw new RuntimeException("Main thread loop unexpectedly exited");
40     }

  以上的main函數代碼中,我們還需要意識到兩個問題:

  1.我們之所以可以在Activity用Handler handler=new Handler()直接創建出來就默認綁定到主線程了,是因為上面的代碼為我們做了綁定主線程的Looper的事情,

  2.主線程的Looper是不能在程序中調用退出的,最后一句代碼看到沒,如果調用的話,就會拋出異常,退出主線程的循環是框架層在調用退出應用程序的時候才調用的

 1     /**
 2      * Initialize the current thread as a looper, marking it as an
 3      * application's main looper. The main looper for your application
 4      * is created by the Android environment, so you should never need
 5      * to call this function yourself.  See also: {@link #prepare()}
 6      */
 7     public static void prepareMainLooper() {
 8         prepare(false);
 9         synchronized (Looper.class) {
10             if (sMainLooper != null) {
11                 throw new IllegalStateException("The main Looper has already been prepared.");
12             }
13             sMainLooper = myLooper();
14         }
15     }
16 
17 
18     private static void prepare(boolean quitAllowed) {
19         if (sThreadLocal.get() != null) {
20             throw new RuntimeException("Only one Looper may be created per thread");
21         }
22         sThreadLocal.set(new Looper(quitAllowed));
23     }

  在ActivityThread的main中調用了 Looper.prepareMainLooper() -> prepare(false) -> sThreadLocal.set(new Looper(quitAllowed)), 這么一來,是不是執行到了上面的Looper構造函數中了?到這里,細心的人會發現這么一個問題:

  問題3:Looper被實例化出來之后並沒有直接返回,而是被set到了ThreadLocal中?

  Handler與Looper是成對出現的,一個子線程發送消息,一個主線程接收消息,那么這邊就涉及到了多線程,線程之間的通訊,是需要保證數據的安全,即數據隔離,所以使用到了ThreadLocal進行線程管理:如A線程在獲取數據時只能獲取A線程所控制的數據,而不能去獲取到B線程中對應的數據,否則就會引起數據不同步,比如A線程數據被B線程數據所覆蓋之類的問題,同時也驗證了一個線程只能關聯一個Looper對象

  所以問題3解決了。最后這個main的結尾,調用了 Looper.loop(); 進行輪詢消息隊列! 是不是很完美了?

  3.2、Handler、Looper、MessageQueue之間的協作

  通過前面的源碼分析,我們已經知道了消息是如果添加到消息隊列了。我們再來看消息的出隊分析。

  以下在Looper輪詢器中的loop()中我們看到這樣一句代碼:Message msg = queue.next(); // might block

 1 /**
 2      * Run the message queue in this thread. Be sure to call
 3      * {@link #quit()} to end the loop.
 4      */
 5     public static void loop() {
 6         final Looper me = myLooper();
 7         if (me == null) {
 8             throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
 9         }
10         final MessageQueue queue = me.mQueue;
11 
12         // Make sure the identity of this thread is that of the local process,
13         // and keep track of what that identity token actually is.
14         Binder.clearCallingIdentity();
15         final long ident = Binder.clearCallingIdentity();
16 
17         for (;;) {
18             Message msg = queue.next(); // might block
19             if (msg == null) {
20                 // No message indicates that the message queue is quitting.
21                 return;
22             }
23 
24             // This must be in a local variable, in case a UI event sets the logger
25             final Printer logging = me.mLogging;
26             if (logging != null) {
27                 logging.println(">>>>> Dispatching to " + msg.target + " " +
28                         msg.callback + ": " + msg.what);
29             }
30 
31             final long traceTag = me.mTraceTag;
32             if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
33                 Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
34             }
35             try {
36                 msg.target.dispatchMessage(msg);
37             } finally {
38                 if (traceTag != 0) {
39                     Trace.traceEnd(traceTag);
40                 }
41             }
42 
43             if (logging != null) {
44                 logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
45             }
46 
47             // Make sure that during the course of dispatching the
48             // identity of the thread wasn't corrupted.
49             final long newIdent = Binder.clearCallingIdentity();
50             if (ident != newIdent) {
51                 Log.wtf(TAG, "Thread identity changed from 0x"
52                         + Long.toHexString(ident) + " to 0x"
53                         + Long.toHexString(newIdent) + " while dispatching to "
54                         + msg.target.getClass().getName() + " "
55                         + msg.callback + " what=" + msg.what);
56             }
57 
58             msg.recycleUnchecked();
59         }
60     }

  所以通過以上代碼,我們可以知道消息的出隊,是在Looper這個輪詢器中的loop()函數通過死循環的方式:for (;;),不斷的通過隊列的next()方法中拿到消息:queue.next(),並且如果隊列消息為null了,就跳出該輪詢。所以問題2是不是已經解決了?

   在出隊過程中,也就是MessageQueue消息隊列中的next()函數中,我們可以知道next()返回的是一個Message消息對象,從函數注釋上來看:當輪詢器 loop 輪詢的時候會返回一條消息且從代碼for (;;)循環的代碼中可以看出,是在這里不斷的拿到消息隊列並返回下一條消息,到這里,我們需要注意的是因為這個消息是可以循環使用的,而且我們可以看到這樣一個native函數調用:nativePollOnce(ptr, nextPollTimeoutMillis);所以我們可以得出消息的循環使用內存是通過C++來維護完成的(這邊因為對native沒有深入研究,所以pass這塊!)

  1 Message next() {
  2         // Return here if the message loop has already quit and been disposed.
  3         // This can happen if the application tries to restart a looper after quit
  4         // which is not supported.
  5         final long ptr = mPtr;
  6         if (ptr == 0) {
  7             return null;
  8         }
  9 
 10         int pendingIdleHandlerCount = -1; // -1 only during first iteration
 11         int nextPollTimeoutMillis = 0;
 12         for (;;) {
 13             if (nextPollTimeoutMillis != 0) {
 14                 Binder.flushPendingCommands();
 15             }
 16 
 17             nativePollOnce(ptr, nextPollTimeoutMillis);
 18 
 19             synchronized (this) {
 20                 // Try to retrieve the next message.  Return if found.
 21                 final long now = SystemClock.uptimeMillis();
 22                 Message prevMsg = null;
 23                 Message msg = mMessages;
 24                 if (msg != null && msg.target == null) {
 25                     // Stalled by a barrier.  Find the next asynchronous message in the queue.
 26                     do {
 27                         prevMsg = msg;
 28                         msg = msg.next;
 29                     } while (msg != null && !msg.isAsynchronous());
 30                 }
 31                 if (msg != null) {
 32                     if (now < msg.when) {
 33                         // Next message is not ready.  Set a timeout to wake up when it is ready.
 34                         nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
 35                     } else {
 36                         // Got a message.
 37                         mBlocked = false;
 38                         if (prevMsg != null) {
 39                             prevMsg.next = msg.next;
 40                         } else {
 41                             mMessages = msg.next;
 42                         }
 43                         msg.next = null;
 44                         if (DEBUG) Log.v(TAG, "Returning message: " + msg);
 45                         msg.markInUse();
 46                         return msg;
 47                     }
 48                 } else {
 49                     // No more messages.
 50                     nextPollTimeoutMillis = -1;
 51                 }
 52 
 53                 // Process the quit message now that all pending messages have been handled.
 54                 if (mQuitting) {
 55                     dispose();
 56                     return null;
 57                 }
 58 
 59                 // If first time idle, then get the number of idlers to run.
 60                 // Idle handles only run if the queue is empty or if the first message
 61                 // in the queue (possibly a barrier) is due to be handled in the future.
 62                 if (pendingIdleHandlerCount < 0
 63                         && (mMessages == null || now < mMessages.when)) {
 64                     pendingIdleHandlerCount = mIdleHandlers.size();
 65                 }
 66                 if (pendingIdleHandlerCount <= 0) {
 67                     // No idle handlers to run.  Loop and wait some more.
 68                     mBlocked = true;
 69                     continue;
 70                 }
 71 
 72                 if (mPendingIdleHandlers == null) {
 73                     mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
 74                 }
 75                 mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
 76             }
 77 
 78             // Run the idle handlers.
 79             // We only ever reach this code block during the first iteration.
 80             for (int i = 0; i < pendingIdleHandlerCount; i++) {
 81                 final IdleHandler idler = mPendingIdleHandlers[i];
 82                 mPendingIdleHandlers[i] = null; // release the reference to the handler
 83 
 84                 boolean keep = false;
 85                 try {
 86                     keep = idler.queueIdle();
 87                 } catch (Throwable t) {
 88                     Log.wtf(TAG, "IdleHandler threw exception", t);
 89                 }
 90 
 91                 if (!keep) {
 92                     synchronized (this) {
 93                         mIdleHandlers.remove(idler);
 94                     }
 95                 }
 96             }
 97 
 98             // Reset the idle handler count to 0 so we do not run them again.
 99             pendingIdleHandlerCount = 0;
100 
101             // While calling an idle handler, a new message could have been delivered
102             // so go back and look again for a pending message without waiting.
103             nextPollTimeoutMillis = 0;
104         }
105     }

   到這邊,如果所有的分析及源碼查看都看懂的話,我們就已經掌握了在整個Handler消息機制中,是如何從一個消息的發送,進行了怎么樣的世界環游,最終如何回到了Handler的handleMessage中的!

  分析到這里為止,如果還沒蒙圈的人會發現,我們在前面提出過的幾個問題都解決了,那么問題1呢?

  子線程發送的消息為什么是在主線程中接收的呢?

  其實我們在前面也已經有提及到了該問題,就是為什么在ActivityThread的main中實例化的Looper對象是被set到了ThreadLocal中。

  在java中,main是不是主線程呢?不需要解釋了吧。看代碼:當前線程中Looper的獲取方式

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     public static @Nullable Looper myLooper() {
6         return sThreadLocal.get();
7     }

  程序一開始在ActivityThread在執行main函數時實例化Looper,然后保存到了ThreadLocal中。而我們在主線程中new了一個Handler,那么Handler默認對應的Looper就是主線程的Looper:通過以上代碼,從ThreadLocal管理中獲取出當前線程(主線程)對應的Looper對象,所以對應的Looper自然也是主線程的Looper,明白了嗎?

  所以主線程的Looper在輪詢出消息隊列MessageQueue中的消息時,就是出於主線程中,這樣問題1是不是就清楚了。

   


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM