###### tags: `Android`, `Thread`, `Handler` # Handler, Message, Looper, Thread 之間的關係 ## 前言 <!-- 我是一個愛思考的人, 有很多技術, 其背後做了什麼事情, 是相當想了解清楚的。只不過會因為當下的時間因素, 就把這些問題排進一個隱形的PrioriyQueue裡。 最近queue pop出來這個題目, 因此就來詳細了解一下, 背後到底發生了什麼事, 並以筆記的形式紀錄下來。 --> 此篇著重在敘述`Handler, Message, Looper, Thread`之間的關係, 因此建議先對以上四位角色有基礎的了解後再繼續閱讀。 ## 主角成員介紹 ### Lopper Lopper 封裝 MessageQueue, Thread ```java= private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); } ``` <!-- Lopper.prepare(): 創建一個Looper, 允許退出(quitAllowed = true) ```java= new Looper(true); ``` --> ### Message 重要成員: ```java= class Message { // ... Handler target; // Message 的目標, 意即Message會被消化掉的地方 Runnable callback; Message next; // Message為 linked 的結構. 主要用於MessageQueue } ``` ### Handler 主要成員: ```java= class Handler { // ... public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) { mLooper = looper; mQueue = looper.mQueue; //用於sendMessage mCallback = callback; mAsynchronous = async; } } ``` ## 關係 ### 創建 Handler 從創建Handler為切入點來說: Handler 創建初期一定會有上述4個主要成員。 如果你選擇了沒有Looper參數的Constructor (比如 `new Handler()`), Handler內部會從`Looper.myLooper()`去獲取Looper 和 MessageQueue 總的來說 :::success :point_right: Looper 會被塞入 Handler ```graphviz digraph dfd2{ node[shape=record] subgraph level0{ } subgraph cluster_level1{ label ="Handler"; proc1 [label="{<f0> Looper|<f1> mThread: Thread\nmQueue: MessageQueue\n\n}" shape=Mrecord]; } } ``` ::: :::warning :dart: `Looper.myLooper()` 方法, 實際上是從Looper 內部的sThreadLocal.get()獲取的。 在該thread上, 若沒有執行`Looper.prepare()`, ThreadLocal會回傳`null`, 因此要確保Handler 創建之前, `Looper.prepare()`有在同一個 thread裡被呼叫到。 ::: ### 透過Handler 傳遞訊息 而每當透過Handler 傳送訊息, 執行 sendMessage / post ... 等方法: 1. Handler 會將 Message 丟進 MessageQueue 裡 ```java= private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg, long uptimeMillis) { msg.target = this; msg.workSourceUid = ThreadLocalWorkSource.getUid(); if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); } ``` :::success ```graphviz digraph dfd2{ node[shape=record] subgraph level0{ msg [label="Message" shape=box]; } subgraph cluster_level1{ label ="Handler"; proc1 [label="{<f0> Looper|<f1> mThread: Thread\nmQueue: MessageQueue\n\n}" shape=Mrecord]; } msg -> proc1:f1 [label="enqueueMessage", fontcolor=red] } ``` ::: :::info :bulb: 如果你是使用`post(@NonNull Runnable r)`, Handler 也會創建一個Message 把 `runnable` 當成`callback` 包起來, 然後再執行`enqueueMessage` ::: 2. 當Thread呼叫`Looper.loop()`時, 會從MessageQueue 不斷拉出Message 來, 送往該Message 記住的 Handler ```java= public static void loop() { // ... for (;;) { Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } // ... try { msg.target.dispatchMessage(msg); // target 為 Handler if (observer != null) { observer.messageDispatched(token, msg); } dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0; } catch (Exception exception) { // ... } // ... } } ``` :::success ```graphviz digraph dfd2{ node[shape=record] thread [label="Thread"] proc1 [label="{<f0> Looper|<f1> mThread: Thread\nmQueue: MessageQueue\n\n}" ]; message [label="Message"] handler [label="Handler"] thread -> proc1 [label=" call looper.loop()", fontcolor=red] proc1:f1 -> message [label=" pop message"] message -> handler [label=" send to target"] } ``` ::: 3. Handler 處理該Message ```java= public void dispatchMessage(@NonNull Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } } ``` 從中我們也順便得知了Message被處理的順序 > 1. message 自身的 Callback > 2. Handler 的 Callback > 3. Handler 的 member function: handleMessage(msg) (不一定會被觸發) ## MainThread 的例子 在追蹤這些Code 時, 追到了ActivityThread 這個Class, 也發現了android app 的入口: ```java= public static void main(String[] args) { // ... Looper.prepareMainLooper(); // ... if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); } // ... Looper.loop(); } ``` 前面有提到, **Looper.prepare()一定要先呼叫**, 而我們平常MainThread 不用呼叫, 是因為剛開始就已經先執行了。