Android Handler揭祕(一)

簡述:

Handler在Android裏面到處可見。一般用於多線程消息交互、主線程刷新、延時/定時處理等。今天來揭開Handler背後的祕密。

PS: 如果有想替代Handler用開源庫的話,大名鼎鼎的RxJava就是幹這事的。

相關代碼:

  1. frameworks/base/core/java/android/os/Handler.java
  2. frameworks/base/core/java/android/os/MessageQueue.java
  3. frameworks/base/core/java/android/os/Looper.java

介紹:

從Handler默認構造函數入手:

110      /**
111       * Default constructor associates this handler with the {@link Looper} for the
112       * current thread.
113       *
114       * If this thread does not have a looper, this handler won't be able to receive messages
115       * so an exception is thrown.
116       */
117      public Handler() {
118          this(null, false);
119      }

從註釋可以看出,Handler綁定了Looper,而Looper又記錄了當前線程等信息,且認爲是Handler跟線程強相關。

找到參數爲(null, false)的方法,

175      /**
176       * Use the {@link Looper} for the current thread with the specified callback interface
177       * and set whether the handler should be asynchronous.
178       *
179       * Handlers are synchronous by default unless this constructor is used to make
180       * one that is strictly asynchronous.
181       *
182       * Asynchronous messages represent interrupts or events that do not require global ordering
183       * with respect to synchronous messages.  Asynchronous messages are not subject to
184       * the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.
185       *
186       * @param callback The callback interface in which to handle messages, or null.
187       * @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for
188       * each {@link Message} that is sent to it or {@link Runnable} that is posted to it.
189       *
190       * @hide
191       */
192      public Handler(Callback callback, boolean async) {
193          if (FIND_POTENTIAL_LEAKS) {
194              final Class<? extends Handler> klass = getClass();
195              if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
196                      (klass.getModifiers() & Modifier.STATIC) == 0) {
197                  Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
198                      klass.getCanonicalName());
199              }
200          }
201  
202          mLooper = Looper.myLooper();
203          if (mLooper == null) {
204              throw new RuntimeException(
205                  "Can't create handler inside thread " + Thread.currentThread()
206                          + " that has not called Looper.prepare()");
207          }
208          mQueue = mLooper.mQueue;
209          mCallback = callback;
210          mAsynchronous = async;
211      }

關鍵代碼mLooper = Looper.myLooper()繼續進去看

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

250      /**
251       * Return the Looper object associated with the current thread.  Returns
252       * null if the calling thread is not associated with a Looper.
253       */
254      public static @Nullable Looper myLooper() {
255          return sThreadLocal.get();
256      }

從sThreadlocal裏面取出Looper。sThreadLocal,看名字意思就可以大概知道線程本地記錄,他可以記錄了一個Looper對象(要詳細瞭解ThreadLocal,可以搜下)。既然是get,一般對應的可以找到set的地方。找遍Looper代碼可以發現只有一個set的地方:

91      /** Initialize the current thread as a looper.
92        * This gives you a chance to create handlers that then reference
93        * this looper, before actually starting the loop. Be sure to call
94        * {@link #loop()} after calling this method, and end it by calling
95        * {@link #quit()}.
96        */
97      public static void prepare() {
98          prepare(true);
99      }
100  
101      private static void prepare(boolean quitAllowed) {
102          if (sThreadLocal.get() != null) {
103              throw new RuntimeException("Only one Looper may be created per thread");
104          }
105          sThreadLocal.set(new Looper(quitAllowed));
106      }

    從代碼可以看出,每個線程只會有一個Looper。prepare只能調用一次,如果調用第二次,就會丟出RuntimeException。繼續看Looper的構造函數:

267      private Looper(boolean quitAllowed) {
268          mQueue = new MessageQueue(quitAllowed);
269          mThread = Thread.currentThread();
270      }

    Looper創建了一個MessageQueue,同時引用了當前線程對象。到這裏,Handler多線程消息交互,就可以推測出,應該就是靠的這個mQueue了(參數quitAllowed用於區別當前線程是否是主線程)。

    我們回到Handler的構造函數往下看,203行會有一個判斷mLooper是否爲空。如果是空,就會丟出異常。這裏是爲了保證在Handler對象初始化的時候,這個mLooper一定綁定過一個線程,也就是一定調用過Looper的prepare方法。在Looper裏面的類介紹裏面也給出了示例用法:

27  /**
28    * Class used to run a message loop for a thread.  Threads by default do
29    * not have a message loop associated with them; to create one, call
30    * {@link #prepare} in the thread that is to run the loop, and then
31    * {@link #loop} to have it process messages until the loop is stopped.
32    *
33    * <p>Most interaction with a message loop is through the
34    * {@link Handler} class.
35    *
36    * <p>This is a typical example of the implementation of a Looper thread,
37    * using the separation of {@link #prepare} and {@link #loop} to create an
38    * initial Handler to communicate with the Looper.
39    *
40    * <pre>
41    *  class LooperThread extends Thread {
42    *      public Handler mHandler;
43    *
44    *      public void run() {
45    *          Looper.prepare();
46    *
47    *          mHandler = new Handler() {
48    *              public void handleMessage(Message msg) {
49    *                  // process incoming messages here
50    *              }
51    *          };
52    *
53    *          Looper.loop();
54    *      }
55    *  }</pre>
56    */

繼續構造函數的第208行,這裏有個關鍵變量。mQueue的賦值:

208          mQueue = mLooper.mQueue;

到這裏,可以總結出:

      同一個線程裏面,可以有多個Handler,最多隻有一個Looper對象,多個Handler用的是同一個MessageQueue。

寫段測試代碼證實下:

package com.fy.platfromdebug;

import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.MessageQueue;
import android.util.Log;

public class TestHandler {
    private static final String TAG = "TestHandler";


    public void test() {
        Handler mainHandler = new Handler(Looper.getMainLooper());
        MessageQueue queue = mainHandler.getLooper().getQueue();
        Log.d(TAG, "mainHandler=" + mainHandler + " looper=" + mainHandler.getLooper() + " queue=" + queue);

        Handler mainHandler2 = new Handler(Looper.getMainLooper());
        MessageQueue queue2 = mainHandler2.getLooper().getQueue();
        Log.d(TAG, "mainHandler2=" + mainHandler2 + " looper=" + mainHandler2.getLooper() + " queue=" + queue2);
    }

    public void test2() {
        HandlerThread handlerThread = new HandlerThread("Test");
        handlerThread.start();
        Handler handler = new Handler(handlerThread.getLooper());
        Log.d(TAG, "handler=" + handler + " looper=" + handler.getLooper() + " queue=" + handler.getLooper().getQueue());
        Handler handler2 = new Handler(handlerThread.getLooper());
        Log.d(TAG, "handler2=" + handler2 + " looper=" + handler2.getLooper() + " queue=" + handler2.getLooper().getQueue());
    }

}

從log輸出,可以證實我們的分析。

2019-01-07 10:26:30.492 22032-22032/com.fy.platfromdebug D/TestHandler: mainHandler=Handler (android.os.Handler) {459885c} looper=Looper (main, tid 2) {4d34765} queue=android.os.MessageQueue@ad45c3a
2019-01-07 10:26:30.493 22032-22032/com.fy.platfromdebug D/TestHandler: mainHandler2=Handler (android.os.Handler) {f6120eb} looper=Looper (main, tid 2) {4d34765} queue=android.os.MessageQueue@ad45c3a
2019-01-07 10:26:30.496 22032-22032/com.fy.platfromdebug D/TestHandler: handler=Handler (android.os.Handler) {524ce48} looper=Looper (Test, tid 1812) {b35f4e1} queue=android.os.MessageQueue@d9c0206
2019-01-07 10:26:30.496 22032-22032/com.fy.platfromdebug D/TestHandler: handler2=Handler (android.os.Handler) {aaee0c7} looper=Looper (Test, tid 1812) {b35f4e1} queue=android.os.MessageQueue@d9c0206

這裏有個疑問,平時我們實例化Handler時,也沒有傳過任何參數,那Handler是綁定的哪個線程呢?我們也沒有調用過Looper.prepare(),代碼也沒丟過異常。

    Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            ...
        }
    };

綁定的線程很好理解,就是實例化代碼運行在哪個線程,就綁定的哪個線程。

Looper.prepare()雖然我們沒調用過,不代表別人沒調用過。上面的代碼一般是運行在主線程,那我們肯定可以找到調用parpare的地方。在Looper裏面搜索調用prepare的地方,發現Looper裏面還有一個方法:

108      /**
109       * Initialize the current thread as a looper, marking it as an
110       * application's main looper. The main looper for your application
111       * is created by the Android environment, so you should never need
112       * to call this function yourself.  See also: {@link #prepare()}
113       */
114      public static void prepareMainLooper() {
115          prepare(false);
116          synchronized (Looper.class) {
117              if (sMainLooper != null) {
118                  throw new IllegalStateException("The main Looper has already been prepared.");
119              }
120              sMainLooper = myLooper();
121          }
122      }

這裏調用了prepare方法,而這個prepareMainLooper在ActivityThread的main方法裏面就調用了。所以前面的Handler綁定的是主線程。

6804      public static void main(String[] args) {
              ...
6822  
6823          Looper.prepareMainLooper();
6824  
              ...
6853      }

到現在爲止,我們已經搞清楚Handler跟線程之前的關係了。

下次我們再來揭祕Handler背後的另一個boss, MessageQueue。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章