Try   HackMD

Android Handler 消息機制

OverView of Content

如有引用參考請詳註出處,感謝 :smile:

Android 是以消息推動系統的機制,所以我們要好好了解 Handler 機制,以下會以使用 Handler 分析源碼 來下手;這篇是分析 Java 層的消息機制

Android Handler 概述

由於 Android 是 消息驅動 的系統,所以要先來介紹 Handler,Handler 機制主要有分為 4 個組成

說明功能
Handler 真正處理事件的地方
Looper 循環檢查是否有要傳輸的事件,從 MessageQueue 中獲取事件,並交給 Handler 處理(假如列隊為空就進入休眠)
MessageQueue 儲存需要做的事情,一般來說止允許保存相同類型的 Object、Message 除存的數據結構為 佇列
Message 需要做的事件
  • 其實一開始會很難發現 Handler & Thread 之間的關係,我們可以先看看這幾個結論在看看程式如何實現它

    1. 一個 Thread 對應一個 Looper
    2. 一個 Looper 對應一個 MessageQueue
    3. 一個 MessageQueue 內有多個 Message
    4. 每個 Message 中最多對應一個 Handler 處理事件
  • Thread & Handler 是一對多的關係

Handler

  • 應用開發時常使用到 Handler 類,它主要有兩個做用

    1. 接收 處理 Message

    2. 將 Message 存入 MessageQueue 中

      Image Not Showing Possible Reasons
      • The image file may be corrupted
      • The server hosting the image is unavailable
      • The image path is incorrect
      • The image format is not supported
      Learn More →

    ​​​​// Handler.java ​​​​public class Handler { ​​​​ ... ​​​​ final Looper mLooper; ​​​​ final MessageQueue mQueue; ​​​​ final Callback mCallback; ​​​​ ... ​​​​ public interface Callback { ​​​​ boolean handleMessage(@NonNull Message msg); ​​​​ } ​​​​ // 通常用來給使用者 Override 處理事件 ​​​​ public void handleMessage(@NonNull Message msg) { ​​​​ } ​​​​ // 分發事件,有三種方式 ​​​​ public void dispatchMessage(@NonNull Message msg) { ​​​​ if (msg.callback != null) { ​​​​ handleCallback(msg); ​​​​ } else { ​​​​ if (mCallback != null) { ​​​​ if (mCallback.handleMessage(msg)) { ​​​​ return; ​​​​ } ​​​​ } ​​​​ handleMessage(msg); ​​​​ } ​​​​ } ​​​​}
  • 在上面分發事件有三種方法,並且 這三種分發方式有 1 先後順序,只要前面被處理,就不會使用後面的方式處理

    1. Message 本身的 callback
    2. 實現 Callback 接口
    3. Override Handler#handleMessage Function

    這種方式就會變成了一種循環

    Image Not Showing Possible Reasons
    • The image file may be corrupted
    • The server hosting the image is unavailable
    • The image path is incorrect
    • The image format is not supported
    Learn More →

  • 透過 Handler 把訊息壓到 MessageQueue,方式有 Post & Send 兩種系列,並且可以 2 控制發送的時間,以下寫幾個常用的

    1. Post 系列:把零散訊息轉為 Message 再用 Send 傳出
      函數 功能
      boolean post(Runnable r) 直接發出訊息
      boolean postAtTime(Runnable, long updatetimeMillis) 在固定(規定)時間再發出該條訊息
    2. Send 系列:參數直接是 Message
      函數 功能
      boolean sendEmptyMessage(int what) 直接發送訊息
      boolean sendMessageArFrontOfQueue(Message msg) 把消息發送到訊息隊列最前面
      boolean sendMessageAtTime(int what, long updatetimeMillis) 在固定(規定)時間再發出該條訊息
      boolean sendMessageDelayed(Message msg, long delayMillis) 延遲幾毫秒後發送該訊息
  • Post 實現方式如下

    ​​​​// Handler.java ​​​​public class Handler { ​​​​ public final boolean post(@NonNull Runnable r) { ​​​​ return sendMessageDelayed(getPostMessage(r), 0); ​​​​ } ​​​​ // 包裝 callback 再傳出 ​​​​ private static Message getPostMessage(Runnable r) { ​​​​ Message m = Message.obtain(); // 取得 Message 資源,避免不斷創建,可以復用資源 ​​​​ m.callback = r; // 直接設定 Runnable 回調函數 ​​​​ return m; ​​​​ } ​​​​ // 最後透過計算 當前時間 + 延長實現,之後就呼叫 sendMessageAtTime 函數 ​​​​ public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) { ​​​​ if (delayMillis < 0) { ​​​​ delayMillis = 0; ​​​​ } ​​​​ return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); ​​​​ } ​​​​ // 在規定時間發送消息 ​​​​ public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) { ​​​​ MessageQueue queue = mQueue; ​​​​ if (queue == null) { ​​​​ // 若是該 Thread 並沒有設定 MessageQueue 則會拋出錯誤 ​​​​ RuntimeException e = new RuntimeException( ​​​​ this + " sendMessageAtTime() called with no mQueue"); ​​​​ Log.w("Looper", e.getMessage(), e); ​​​​ return false; ​​​​ } ​​​​ return enqueueMessage(queue, msg, uptimeMillis); ​​​​ } ​​​​}

發送訊息方法

名稱 參數 : 回傳 解釋
post Runnable : boolean 接收一個 Runnable 對象作為 Message
postAtTime Runnable, long : boolean 接收一個 Runnable 對象作為 Message,並在等待的規定時間發送 Message
sendEmptyMessage int : boolean 發送一個使用者規定的 int 訊息,在由使用者自己處理
sendMessageAtTime Messge, int : boolean 發送一個使用者規定的 int 訊息,在由使用者自己處理
sendMessageDelayed Messge, long : boolean 在等待的規定時間後 (延遲) 發送 Message

Post & Send

  • 從上面可以看出上面主要有 Post & Send
  • 而他們的差別在 Send 內直接發送 Message 訊息 在設定進 Message 內的屬性 what,Post 則是包裝過後在調用 Send 的方法本質是一樣的
// Send ... public final boolean sendEmptyMessageDelayed(int what, long delayMillis) { Message msg = Message.obtain(); msg.what = what; return sendMessageDelayed(msg, delayMillis); } // Post ... public final boolean post(Runnable r) { return sendMessageDelayed(getPostMessage(r), 0); } private static Message getPostMessage(Runnable r) { Message m = Message.obtain(); m.callback = r; return m; } // 最終都會發至 sendMessageAtTime public boolean sendMessageAtTime(Message msg, long uptimeMillis) { MessageQueue queue = mQueue; if (queue == null) { // 1. RuntimeException e = new RuntimeException( this + " sendMessageAtTime() called with no mQueue"); Log.w("Looper", e.getMessage(), e); return false; } return enqueueMessage(queue, msg, uptimeMillis); }
  1. 一般來說每個 Thread 都要有一個 MessageQueue 才可以處理 Message

處理 Message

  • 可透過重載 Handler 中的 dispatchMessagehandleMessage這兩個函數作為 Handler 處理 Message

  • 從 dispatchMessage 可看出它預設處理 Message 的順序
    1. msg.callback (Runnable)
    2. mCallback (interface)
    3. handleMessage (method)
    ​​​​ public void handleMessage(Message msg) { ​​​​ // 使用者覆寫 ​​​​ } ​​​​ /** ​​​​ * Handle system messages here. ​​​​ */ ​​​​ public void dispatchMessage(Message msg) { ​​​​ // callback 優先及最高 ​​​​ if (msg.callback != null) { // msg.callback is Runnable ​​​​ handleCallback(msg); ​​​​ } else { ​​​​ if (mCallback != null) { ​​​​ if (mCallback.handleMessage(msg)) { ​​​​ return; ​​​​ } ​​​​ } ​​​​ handleMessage(msg); ​​​​ } ​​​​ }

runWithScissors 傳同步訊息

  • 舉例:以下是 WMS 在初始化時就使用 (DisplayThread) Handler#runWithScissors 這個函數來達到先執行 Runnable 任務的功能 (如果不先創建 WMS 則後面使用則會出問題)

    ​​​​// DisplayThread.java ​​​​public final class DisplayThread extends ServiceThread { // ServiceThread 會初始化 Loop ​​​​ ​​​​ private static DisplayThread sInstance; ​​​​ private static Handler sHandler; ​​​​ ​​​​ ... 省略部分 ​​​​ ​​​​ private static void ensureThreadLocked() { ​​​​ if (sInstance == null) { ​​​​ sInstance = new DisplayThread(); ​​​​ sInstance.start(); ​​​​ sInstance.getLooper().setTraceTag(Trace.TRACE_TAG_SYSTEM_SERVER); ​​​​ ​​​​ // 創建 Handler 對象 (其中的 Looper 是父類創建並初始化) ​​​​ sHandler = new Handler(sInstance.getLooper()); ​​​​ } ​​​​ } ​​​​ ​​​​ ​​​​ public static Handler getHandler() { ​​​​ synchronized (DisplayThread.class) { ​​​​ ensureThreadLocked(); ​​​​ return sHandler; ​​​​ } ​​​​ } ​​​​} ​​​​// ---------------------------------------------------------- ​​​​// WindowManagerService.java ​​​​ private static WindowManagerService sInstance; ​​​​ ​​​​ static WindowManagerService getInstance() { ​​​​ return sInstance; ​​​​ } ​​​​ @VisibleForTesting ​​​​ public static WindowManagerService main(final Context context, final InputManagerService im, ​​​​ final boolean showBootMsgs, final boolean onlyCore, WindowManagerPolicy policy, ​​​​ ActivityTaskManagerService atm, DisplayWindowSettingsProvider ​​​​ displayWindowSettingsProvider, Supplier<SurfaceControl.Transaction> transactionFactory, ​​​​ Supplier<Surface> surfaceFactory, ​​​​ Function<SurfaceSession, SurfaceControl.Builder> surfaceControlFactory) { ​​​​ // getHandler 就是 Handler 對象 ​​​​ DisplayThread.getHandler().runWithScissors(() -> ​​​​ ​​​​ // 創建 WindowManagerService 對象 ​​​​ ​​​​ sInstance = new WindowManagerService(context, im, showBootMsgs, onlyCore, policy, ​​​​ atm, displayWindowSettingsProvider, transactionFactory, surfaceFactory, ​​​​ surfaceControlFactory), 0); ​​​​ return sInstance; ​​​​ }
  • 這裡的重點是 Handler 中的 runWithScissors 函數: 1 比對 Looper 若是就是 Handler 中的 Thread,則直接執行,2 創建 BlockingRunnable 對象 (主要分析 BlockingRunnable)

    ​​​​// Handler.java ​​​​public class Handler { ​​​​ ... 省略部分 ​​​​ final Looper mLooper; ​​​​ public final boolean runWithScissors(@NonNull Runnable r, long timeout) { ​​​​ // 判空 ​​​​ if (r == null) { ​​​​ throw new IllegalArgumentException("runnable must not be null"); ​​​​ } ​​​​ // 時間檢查 ​​​​ if (timeout < 0) { ​​​​ throw new IllegalArgumentException("timeout must be non-negative"); ​​​​ } ​​​​ // 若當前 Handler 使用的 Looper (mLooper) 就是 Looper#myLooper ​​​​ // 也就是比對 Thread (因為一個 Thread 只有一個 Looper) ​​​​ if (Looper.myLooper() == mLooper) { // 使用到當前 Thread ​​​​ r.run(); // 直接執行 ​​​​ return true; ​​​​ } ​​​​ // 創建堵塞 Runnable 對象 ​​​​ BlockingRunnable br = new BlockingRunnable(r); ​​​​ ​​​​ // 分析 postAndWait 方法 ​​​​ return br.postAndWait(this, timeout); ​​​​ } ​​​​}
  • BlockingRunnable 是 Handler 中的內部類

    ​​​​// Handler.java ​​​​public class Handler { ​​​​ private static final class BlockingRunnable implements Runnable { ​​​​ private final Runnable mTask; ​​​​ private boolean mDone; ​​​​ public BlockingRunnable(Runnable task) { ​​​​ mTask = task; ​​​​ } ​​​​ @Override ​​​​ public void run() { ​​​​ try { ​​​​ mTask.run(); // 跑使用者的任務 ​​​​ } finally { ​​​​ synchronized (this) { ​​​​ mDone = true; ​​​​ notifyAll(); // 喚醒當前對象所有的鎖 ​​​​ } ​​​​ } ​​​​ } ​​​​ public boolean postAndWait(Handler handler, long timeout) { ​​​​ // 先丟入任務 Queue,判斷是否成功 ​​​​ if (!handler.post(this)) { ​​​​ return false; ​​​​ } ​​​​ synchronized (this) { ​​​​ if (timeout > 0) { ​​​​ // 計算到期時間 ​​​​ final long expirationTime = SystemClock.uptimeMillis() + timeout; ​​​​ while (!mDone) { ​​​​ long delay = expirationTime - SystemClock.uptimeMillis(); ​​​​ if (delay <= 0) { ​​​​ return false; // timeout ​​​​ } ​​​​ try { ​​​​ wait(delay); // wait 指定 delay 的時間後喚醒當前對象 ​​​​ } catch (InterruptedException ex) { ​​​​ } ​​​​ } ​​​​ } else { ​​​​ while (!mDone) { ​​​​ try { ​​​​ wait(); // wait 當前對象 ​​​​ } catch (InterruptedException ex) { ​​​​ } ​​​​ } ​​​​ } ​​​​ } ​​​​ return true; ​​​​ } ​​​​ } ​​​​}

MessageQueue & Message

  • 如其名,它是一個數據結構佇列 FIFO,它透過 本地方法 (Native) 內存指針創建 Queue
private native static long nativeInit(); MessageQueue(boolean quitAllowed) { mQuitAllowed = quitAllowed; mPtr = nativeInit(); }

Queue 方法

名稱 參數 : 回傳 解釋
enqueueMessage Message, long : boolean 在指定時間內壓入佇列中
next void : Message 元素拉出佇列
removeMessages Handler, int, Object : void 移除特定元素
removeMessages Handler, Runnable, Object : void 移除特定元素

MessageQueue

  • MessageQueue 因為是 Queue(數據結構) 所以有所謂的 FIFO,MessageQueue 有以下功能
    1. 新建列隊
      由建構函數 中的 nativeInit 方法組成

      ​​​​​​​​// /android/os/MessageQueue.java ​​​​​​​​ ​​​​​​​​MessageQueue(boolean quitAllowed) { ​​​​​​​​ mQuitAllowed = quitAllowed; ​​​​​​​​ mPtr = nativeInit(); ​​​​​​​​} ​​​​​​​​ ​​​​​​​​private native static int nativeInit();
    2. 元素入隊列 enqueueMessage

      ​​​​​​​​// /android/os/MessageQueue.java ​​​​​​​​ ​​​​​​​​boolean enqueueMessage(Message msg, long when) { ​​​​​​​​ ... ​​​​​​​​}
    3. 元素出隊列 next

      ​​​​​​​​// /android/os/MessageQueue.java ​​​​​​​​ ​​​​​​​​Message next() { ​​​​​​​​ ... ​​​​​​​​}
    4. 刪除元素

      ​​​​​​​​// /android/os/MessageQueue.java ​​​​​​​​ ​​​​​​​​void removeMessages(Handler h, int what, Object object) { ​​​​​​​​ ... ​​​​​​​​} ​​​​​​​​ ​​​​​​​​void removeMessages(Handler h, Runnable r, Object object) { ​​​​​​​​ ... ​​​​​​​​}
    5. 銷毀列隊,透過本地函數 nativeDestory 銷毀一個 MessageQueue

      ​​​​​​​​private native static void nativeDestroy(int ptr);

MessageQueue 創建

連接於 Looper 的建構函數,當 Looper 創建時 MessageQueue 就一同創建

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

Looper 透過 MessageQueue 取得 Message

  • Looper 透過 MessageQueue#next 函數,就可以取得下一條要發送的訊息
    • next 函數是一個無限循環的函數,所以取不到訊息可能會 Block
    ​​​​// MessageQueue.java ​​​​ Message next() { ​​​​ // 若已經被 depose mPtr 就會被設定為 0 ​​​​ final long ptr = mPtr; ​​​​ if (ptr == 0) { ​​​​ return null; ​​​​ } ​​​​ int pendingIdleHandlerCount = -1; // -1 only during first iteration ​​​​ // 下一個訊息的時間 ​​​​ int nextPollTimeoutMillis = 0; ​​​​ // 進入無限循環,也就是說 next 會導致 block ​​​​ for (;;) { ​​​​ ... 省略部分 ​​​​ synchronized (this) { ​​​​ // Try to retrieve the next message. Return if found. ​​​​ final long now = SystemClock.uptimeMillis(); ​​​​ Message prevMsg = null; // 上一個訊息 ​​​​ Message msg = mMessages; // 當前訊息 ​​​​ if (msg != null && msg.target == null) { // target 就是 Handler ​​​​ // 找到一個可發送的 Message ​​​​ do { ​​​​ prevMsg = msg; ​​​​ msg = msg.next; // 切換到下一個 Message ​​​​ } while (msg != null && !msg.isAsynchronous()); ​​​​ } ​​​​ // 再次判斷 Message ​​​​ if (msg != null) { ​​​​ // 判斷時間 ​​​​ if (now < msg.when) { // 該訊息的時間還沒到 ​​​​ // Next message is not ready. Set a timeout to wake up when it is ready. ​​​​ // 計算剩餘時間 ​​​​ nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE); ​​​​ } else { ​​​​ // 時間到,並且有 Message ​​​​ mBlocked = false; ​​​​ if (prevMsg != null) { ​​​​ prevMsg.next = msg.next; // 上一個 Message 就是當前 Message ​​​​ } else { ​​​​ mMessages = msg.next; //切換到下一個要用的 Message ​​​​ } ​​​​ // 斷開連結 !! 好讓 GC 偵測到後回收 !!! (可達性分析) ​​​​ msg.next = null; ​​​​ ... debug 訊息 ​​​​ msg.markInUse(); // 標記該 Message 已經被使用過 ​​​​ return msg; ​​​​ } ​​​​ } else { ​​​​ // No more messages. ​​​​ nextPollTimeoutMillis = -1; ​​​​ } ​​​​ // Process the quit message now that all pending messages have been handled. ​​​​ if (mQuitting) { // 檢查是否退出 ​​​​ dispose(); ​​​​ return null; ​​​​ } ​​​​ ... 省略 IdleHandler (下一小節會說明) ​​​​ } ​​​​ ... 省略部分 ​​​​ }

IdleHandler - 偵測閒置

  • IdleHandler 是 MessageQueue 內的一個 interface,它可以在 MessageQueue 沒有消息時讓使用者知道,並決定如何處理

    ​​​​// MessageQueue.java ​​​​ public static interface IdleHandler { ​​​​ // 返回 true,會讓你設定的接口繼續存在 ​​​​ // 返回 false 則會移除你設定的接口 ​​​​ boolean queueIdle(); ​​​​ }
  • 同樣分析 next 方法,可以看到 一個 MessageQueue 最多設定 4 個 IdleHandler

    ​​​​// MessageQueue.java ​​​​ Message next() { ​​​​ // 若已經被 depose mPtr 就會被設定為 0 ​​​​ final long ptr = mPtr; ​​​​ if (ptr == 0) { ​​​​ return null; ​​​​ } ​​​​ int pendingIdleHandlerCount = -1; // -1 only during first iteration ​​​​ // 下一個訊息的時間 ​​​​ int nextPollTimeoutMillis = 0; ​​​​ // 進入無限循環,也就是說 next 會導致 block ​​​​ for (;;) { ​​​​ ... 省略部分 ​​​​ synchronized (this) { ​​​​ ... 省略部分 ​​​​ // 檢查是否有設置 IdleHandler ​​​​ if (pendingIdleHandlerCount < 0 ​​​​ && (mMessages == null || now < mMessages.when)) { ​​​​ pendingIdleHandlerCount = mIdleHandlers.size(); ​​​​ if (pendingIdleHandlerCount <= 0) { ​​​​ // No idle handlers to run. Loop and wait some more. ​​​​ mBlocked = true; ​​​​ continue; ​​​​ } ​​​​ // 最多設置 4 個 Idle Handler ​​​​ if (mPendingIdleHandlers == null) { ​​​​ mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)]; ​​​​ } ​​​​ mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers); ​​​​ } ​​​​ ​​​​ // 循環 ​​​​ for (int i = 0; i < pendingIdleHandlerCount; i++) { ​​​​ final IdleHandler idler = mPendingIdleHandlers[i]; ​​​​ mPendingIdleHandlers[i] = null; // release the reference to the handler ​​​​ boolean keep = false; ​​​​ try { ​​​​ // 當沒有訊息時通知接口 ​​​​ keep = idler.queueIdle(); ​​​​ } catch (Throwable t) { ​​​​ Log.wtf(TAG, "IdleHandler threw exception", t); ​​​​ } ​​​​ // 返回 false 就移除 ​​​​ if (!keep) { ​​​​ synchronized (this) { ​​​​ mIdleHandlers.remove(idler); ​​​​ } ​​​​ } ​​​​ } ​​​​ ... 省略部分 ​​​​ } ​​​​ }

Looper

  • 它是組成消息的重要關鍵 (Handler、Message、MessageQueue),它的角色是驅動
名稱 參數 : 回傳 解釋
myLooper void : Looper 以當前線程為主,取得當前 Thread 的 Looper
getMainLooper void : Looper 取得主線程的 Looper

Looper - ThreadLocal

  • 最後 Looper 則是讓整個 Hanler 機制循的動力,有 Looper 的推動 Handler 才能正常接收、發送資訊

  • Looper 中包含了一個 MessageQueue(在建構函數中建構的)

    ​​​​// ava/android/os/Looper.java ​​​​public final class Looper { ​​​​ // 私有函數 ​​​​ private Looper(boolean quitAllowed) { ​​​​ mQueue = new MessageQueue(quitAllowed); ​​​​ mThread = Thread.currentThread(); ​​​​ } ​​​​}
  • 當你每創建一個線程就必須為這個線程準備 Looper,否則在 Handler 發送消息時就會 crush (上面有說到會檢查),創建主要有以下步驟

    1. Looper 準備工作(prepare 函數)
    2. 創建 Handler 發送 & 接收消息
    3. Looper 開始運作(loop 函數)
    ​​​​class LooperThread extends Thread { ​​​​ private Handler handler; ​​​​ public void run() { ​​​​ // 1 ​​​​ Looper.prepare(); ​​​​ // 2. ​​​​ handler = new Handler() { ​​​​ @Override ​​​​ public void handleMessage(Message msg) { ​​​​ //... 處理 Message ​​​​ } ​​​​ } ​​​​ // 3. ​​​​ Looper.loop(); ​​​​ } ​​​​}
    • 不需要準備 Thread ?
      其實 Thread 是也是有使用,不過它由 ThreadLocal 做管理,細節可以參考另外一篇 文章
  • ThreadLocal 這裡大概提及,ThreadLocal 也就是線程隔離,使用線程 Thread 作為 Key 儲存,而 Value 就是你要拷貝到每個線程的數據(每個子線程都持有一個數據)

    ​​​​// /android/os/Looper.java ​​​​public final class Looper { ​​​​ // 拷貝線程的數據是 Looper ​​​​ static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); ​​​​ public static void prepare() { ​​​​ prepare(true); ​​​​ } ​​​​ private static void prepare(boolean quitAllowed) { ​​​​ if (sThreadLocal.get() != null) // 一個線程只能 prepare 一次 ​​​​ throw new RuntimeException("Only one Looper may be created per thread"); ​​​​ } ​​​​ // 為當前 Thread 設定 Looper ​​​​ sThreadLocal.set(new Looper(quitAllowed)); ​​​​ } ​​​​}
  • 這裡要注意 1 ThreadLocal 是泛型,2 在 Looper 裡面就是一個靜態變數

  • 當你建立一個 Handler 時,在 Handler construct 就會透過當前的線程取得 Looper

    ​​​​// Handler ​​​​ public Handler(@Nullable Callback callback, boolean async) { ​​​​ ... ​​​​ mLooper = Looper.myLooper(); ​​​​ if (mLooper == null) { ​​​​ throw new RuntimeException( ​​​​ "Can't create handler inside thread " + Thread.currentThread() ​​​​ + " that has not called Looper.prepare()"); ​​​​ } ​​​​ mQueue = mLooper.mQueue; ​​​​ mCallback = callback; ​​​​ mAsynchronous = async; ​​​​ } ​​​​// ----------------------------------------------------------------- ​​​​// Looper ​​​​ public static @Nullable Looper myLooper() { ​​​​ // sThreadLocal 是靜態的,會透過當前 Thread 作為 Key 取得 Looper ​​​​ return sThreadLocal.get(); ​​​​ }

Looper & WorkThread

當一個普通線程要使用 Handler 時 必須要有 Looper 的預前準備工作

public class MyThread extends Thread { private Handler handler; @SuppressLint("HandlerLeak") @Override public void run() { Looper.prepare(); //"1. " handler = new Handler() { //"2. " @Override public void handleMessage(@NonNull Message msg) { } }; Looper.loop(); //"3. " } }
  1. Looper 預前準備
//"a. " static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); public static void prepare() { prepare(true); } private static void prepare(boolean quitAllowed) { // 只要內部有就不會創建 Loop if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } //"b. " sThreadLocal.set(new Looper(quitAllowed)); } //Looper Constract... private Looper(boolean quitAllowed) { //"c. " mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }

a. 它有一個很重要的屬性 static final ThreadLocal,它是一個 靜態變量,意味著每個線程共享此屬性,但 ThreadLocal 是特殊的變量,讓每一個 Thread 都有一個 Looper 對象

b. 可看出 prepare 時它內部會自己創建一個 Looper 對象,並且 Looper 的建構函數也是私有的

c. Looper 被創建時就會自己準備一個 MessageQueue

  1. 創建處理消息的 Handler
  2. Looper 開始循環運作

Looper & Handler

Looper 跟 Handler 的關係可以從 new Handler() 這個建構函數 中看出,它是透過 myLooper 方法,透過目前的線程在 ThreadLocal 中 取得該當前線程所擁有的 Looper

這也就是上面在準備時為何要調用 Looper.prepare() 這個方法,如果沒準備就不一定會有 Looper

// Handler construct public Handler() { this(null, false); } public Handler(Callback callback, boolean async) { ... 忽略 // 取得當前線程的 Loop mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async; } //Looper... public static @Nullable Looper myLooper() { return sThreadLocal.get(); }

Looper & UI Thread

  • Android 中 Activity 的主線程是 ActivityThread's main() (frameworks/base/core/java/android/app/ActivityThread.java)
public static void main(String[] args) { ... Looper.prepareMainLooper(); //"1. " ActivityThread thread = new ActivityThread(); thread.attach(false); if (sMainThreadHandler == null) { //"2. " sMainThreadHandler = thread.getHandler(); } ... // End of event ActivityThreadMain. ... Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited"); }
  1. 主線程的 Looper 是不可以關閉的,並且使用類同步

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

    sMainLooper = myLooper(); 把目前的 Thread 作為內部靜態類維護,並可以透過,getMainLooper 隨時取得

    ​​​​public static Looper getMainLooper() { ​​​​ synchronized (Looper.class) { ​​​​ return sMainLooper; ​​​​ } ​​​​ }
    • 這樣主線程可以隨時訪問其他 Looper (透過 myLooper() 方法)
  2. 一般線程必須自己建造 Handler 處理訊息,不過其實 ActivityThread 內部已經有自己的 Handler 實現 (並且不能 Override)

    ​​​​// ActivityThread.java ​​​​final Handler getHandler() { ​​​​ return mH; ​​​​ } ​​​​// 屬性... ​​​​final H mH = new H(); ​​​​// Handler 實現 ​​​​private class H extends Handler { ​​​​... ​​​​}
  3. Looper#loop,首先取得目前線程所擁有的 Looper,再取出 Looper 內部有的 MessageQueue,最後無限循環整個消息對列

    ​​​​// Looper.java ​​​​public static void loop() { ​​​​ final Looper me = myLooper(); ​​​​ ... ​​​​ final MessageQueue queue = me.mQueue; ​​​​ ... ​​​​ for (;;) { ​​​​ // ​​​​ Message msg = queue.next(); // might block ​​​​ if (msg == null) { ​​​​ // No message indicates that the message queue is quitting. ​​​​ return; ​​​​ } ​​​​ ... ​​​​ try { ​​​​ // 解決是誰再呼叫 dispatchMessage,target 為 MessageQueue 中的 Handler ​​​​ msg.target.dispatchMessage(msg); ​​​​ end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis(); ​​​​ } finally { ​​​​ if (traceTag != 0) { ​​​​ Trace.traceEnd(traceTag); ​​​​ } ​​​​ } ​​​​ ... ​​​​ msg.recycleUnchecked(); ​​​​ } ​​​​ }

ThreadLocal

  • ThreadLocal 的功能主要是做到線程隔離 (用線程隔離數據)簡單來說可以把 ThreadLocal 當作一個 Map (但實際來說它並不是 Map),它的 Key 是 Thread,Value 是一個泛型

  • 詳細 ThreadLocal 分析有寫在 Java 多線程-進階

    ​​​​// 前面 Looper.prepare() 內部就有使用到 set() 方法 ​​​​ public void set(T value) { ​​​​ Thread t = Thread.currentThread(); //"1. " ​​​​ ThreadLocalMap map = getMap(t); ​​​​ if (map != null) ​​​​ map.set(this, value); ​​​​ else ​​​​ createMap(t, value); ​​​​ } ​​​​// 前面 Handler() 建構函數內部就有使用到 get() 方法 ​​​​public T get() { ​​​​ Thread t = Thread.currentThread(); //"2. " ​​​​ ThreadLocalMap map = getMap(t); ​​​​ if (map != null) { ​​​​ ThreadLocalMap.Entry e = map.getEntry(this); ​​​​ if (e != null) { ​​​​ @SuppressWarnings("unchecked") ​​​​ T result = (T)e.value; ​​​​ return result; ​​​​ } ​​​​ } ​​​​ return setInitialValue(); ​​​​ }
    1. 它使用目前呼叫它的 Thread.currentThread 作為 Key 存入,Value 是泛型

    2. 依照當前線程 (currentThread),取的 Value

  • 消息機制就是通過每個 Thread 儲存不同的 Looper 在 ThreadLocal 中,當有需要時就已 Thread 作為 key 來取得 Looper

結論

Handler & Thread

  • 從上面可以看出 Handler & Thread 的關係是建立在,ThreadLocal 取得 Looper,而 Handler 建構函數又取得當前 Thread

  • 一個 Thread 對應一個 Looper,一個 Looper 對應一個 MessageQueue

Appendix & FAQ

tags: Android 進階