- 《Android Handler機(jī)制》
- 《Android Handler分析(一) Handler和Message詳解》
- 《Android Handler分析 (二) MessageQueue詳解》
- 《Android Handler分析 (三) Looper詳解和Handler其他知識(shí)》
- 《Android 中的 HandlerThread 類詳解》
作為一個(gè)Android程序員,我想大家都知道在做一些比較耗時(shí)的操作的時(shí)候都不會(huì)放在主線程,比如網(wǎng)絡(luò)請(qǐng)求逼泣、數(shù)據(jù)庫(kù)操作等(尤其是網(wǎng)絡(luò)請(qǐng)求撮胧,在Android4.0之后強(qiáng)制不能放在主線程中執(zhí)行一罩,否則拋出android.os.NetworkOnMainThreadException異常)廊驼,而更新UI的操作的都是由UI線程處理犹芹。那么有這樣一個(gè)需求熙涤,我在一個(gè)新的線程中獲取到數(shù)據(jù)阁苞,然后設(shè)置到界面上困檩,這就涉及到了兩個(gè)線程,用于更新界面的UI線程不知道什么時(shí)候數(shù)據(jù)獲取完成可以更新界面了那槽,那怎么辦呢悼沿?Google就給我們提供了一個(gè)解決方案,也就是我們今天主要說(shuō)明的內(nèi)容——Android中的Handler消息機(jī)制骚灸。
首先來(lái)一個(gè)簡(jiǎn)單使用:
private static final int HANDLER_TEST_WHAT = 0x01; // 定義測(cè)試what
// 創(chuàng)建Handler對(duì)象糟趾,并重寫(xiě)handleMessage()方法處理消息
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
int what = msg.what; // 獲取消息的what
// 通過(guò)what對(duì)消息進(jìn)行判斷,不同消息不同的處理
if(what == HANDLER_TEST_WHAT){
// 處理消息
}
}
};
// 創(chuàng)建一個(gè)子線程發(fā)送消息
new Thread(){
@Override
public void run() {
Message message = Message.obtain(); // 創(chuàng)建一個(gè)消息對(duì)象甚牲,使用obtain()重用回收的消息對(duì)象
message.what = HANDLER_TEST_WHAT; // 設(shè)置Message的 what
handler.sendMessage(message); // 發(fā)送一個(gè)消息
}
}.start();
上面就是一個(gè)簡(jiǎn)單的例子义郑,在子線程發(fā)送一個(gè)消息,然后在主線程就可以對(duì)消息進(jìn)行處理丈钙,當(dāng)然包括更新UI或者其他操作非驮。
下面我們就從源碼的角度查看與分析一下Handler消息機(jī)制,他是怎樣將一個(gè)子線程的數(shù)據(jù)傳遞到主線程中的雏赦。
介紹Handler機(jī)制之前劫笙,首先了解幾個(gè)概念:
- Message:消息,理解為線程間通訊的數(shù)據(jù)單元星岗。例如后臺(tái)線程在處理數(shù)據(jù)完畢后需要更新UI填大,則可發(fā)送一條包含更新信息的Message給UI線程。
- MessageQueue:消息隊(duì)列俏橘,用來(lái)存放通過(guò)Handler發(fā)布的消息并按時(shí)間進(jìn)行排序允华,按照先進(jìn)先出執(zhí)行。
- Handler:Handler是Message的主要處理者敷矫,負(fù)責(zé)將Message添加到消息隊(duì)列以及對(duì)消息隊(duì)列中的Message進(jìn)行處理例获。
- Looper:輪詢器,扮演MessageQueue和Handler之間橋梁的角色曹仗,循環(huán)取出MessageQueue里面的Message榨汤,并交付給相應(yīng)的Handler進(jìn)行處理。
消息機(jī)制分析
從上面的示例中怎茫,我們先創(chuàng)建了一個(gè)Handler對(duì)象并且重寫(xiě)了 handleMessage()
方法用來(lái)處理接收到的消息收壕,而消息的發(fā)送是通過(guò) Handler 的sendMessage()
方法。也可以說(shuō)轨蛤,Handler機(jī)制是由 sendMessage()
方法開(kāi)始蜜宪,到 handleMessage()
方法結(jié)束,現(xiàn)在我們就來(lái)一步一步看看數(shù)據(jù)是怎樣傳遞的祥山,線程又是怎樣切換的圃验。
首先看看創(chuàng)建 Handler
最終調(diào)用的是 帶兩個(gè)參數(shù) 的構(gòu)造方法,如下:
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;
}
說(shuō)明:
-
Looper.myLooper():獲取當(dāng)前線程的 Looper 對(duì)象缝呕,也就是線程需要是一個(gè) Looper 線程澳窑,主線程就是一個(gè) Looper 線程斧散,主線程為什么是一個(gè)Looper線程以及怎樣將一個(gè)線程變?yōu)?Looper 線程,在《Android Handler分析 (三) Looper詳解和Handler其他知識(shí)》中有說(shuō)到摊聋。
public static @Nullable Looper myLooper() { return sThreadLocal.get(); }
-
mLooper.mQueue:就是一個(gè) MessageQueue 對(duì)象鸡捐,他是在 Looper 構(gòu)造方法中初始化的
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }
我們創(chuàng)建了 Handler 對(duì)象之后,然后通過(guò) Handler 的 sendMessage()
方法發(fā)送一個(gè) Message麻裁,接下來(lái)看看 sendMessage()
方法做了寫(xiě)什么事
Handler#sendMessage() 方法
public final boolean sendMessage(@NonNull Message msg) {
return sendMessageDelayed(msg, 0);
}
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) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg, long uptimeMillis) {
// 注意這行代碼箍镜,將 Message 的 target 變量賦值成 this,也就是當(dāng)前 Handler 對(duì)象
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
說(shuō)明:
-
sendMessage()
方法調(diào)用了一些 Message 類自身的發(fā)送方法煎源,主要是處理消息發(fā)送時(shí)間(立馬發(fā)送還是延遲發(fā)送等) - 最終調(diào)用
enqueueMessage()
方法色迂,在這個(gè)方法中又調(diào)用了queue.enqueueMessage(msg, uptimeMillis)
,queue 就是 MessageQueue手销,所以查看一下 MessageQueue 中的enqueueMessage()
方法 - 重點(diǎn)注意
enqueueMessage()
方法中的msg.target = this
代碼脚草,將 Message 的target
變量賦值成this
,也就是當(dāng)前 Handler對(duì)象
MessageQueue#enqueueMessage() 方法
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
這個(gè)方法所做的事情就是將 Message 對(duì)象根據(jù)發(fā)送時(shí)間添加到隊(duì)列中原献,方法的具體分析在 《Android Handler分析 (二) MessageQueue詳解》中。
Message 消息對(duì)象添加到 MessageQueue 中之后埂淮,在哪里取出來(lái)姑隅,并且傳遞到 handleMessage() 方法呢?
這里我們就有必要先說(shuō)一下 Looper 線程了倔撞,一個(gè)類變?yōu)長(zhǎng)ooper線程并且能處理消息讲仰,主要有兩步:
- 調(diào)用 Looper.prepare() 方法
- 調(diào)用 Looper.loop() 方法開(kāi)始輪詢
至于這兩個(gè)方法具內(nèi)容是什么我們這里就不說(shuō)了,在《Android Handler分析 (三) Looper詳解和Handler其他知識(shí)》 中有分析痪蝇,只需要知道 loop()
方法最重要的作用就是不斷的循環(huán)調(diào)用 MessageQueue 的 next()
方法去獲取 Message 鄙陡,然后進(jìn)行處理,而 MessageQueue 的 next()
方法的消息分析在 《Android Handler分析 (二) MessageQueue詳解》中躏啰,它的主要作用就是按照順序從隊(duì)列里面取出 Message趁矾。
所以我們直接看 MessageQueue.next()
取到消息之后 Looper.loop()
方法是怎樣處理的,核心代碼如下:
public static void loop() {
final Looper me = myLooper();
final MessageQueue queue = me.mQueue;
Message msg = queue.next();
try {
msg.target.dispatchMessage(msg);
} catch (Exception exception) {
} finally {
}
}
說(shuō)明:
通過(guò)獲取的 Message 對(duì)象的變量 target
, 調(diào)用它的 dispatchMessage()
方法给僵,并且將 msg
對(duì)象傳遞給 dispatchMessage()
方法毫捣。在上面,我們已經(jīng)知道了 target
變量表示的就是 Handler帝际,所以這里調(diào)用的就是 Handler 的 dispatchMessage()
方法了蔓同。
Handler#dispatchMessage() 方法
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
在 dispatchMessage()
方法中調(diào)用了 handleMessage(msg)
方法并傳遞 Message,這個(gè)方法我們?cè)趧?chuàng)建 Handler 時(shí)重寫(xiě)了蹲诀。所以就走到了我們自己處理消息的部分斑粱。因?yàn)槲覀兊倪@個(gè)Handler是在主線程創(chuàng)建的并且綁定的也是主線程的Looper,所以這個(gè)方法就在主線程當(dāng)中執(zhí)行了脯爪。