###### 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 不用呼叫, 是因為剛開始就已經先執行了。