概述
Android 的消息機(jī)制主要是指 Handler 的運(yùn)行機(jī)制逞力。Android 規(guī)定只有主線(xiàn)程可以訪(fǎng)問(wèn) UI ,子線(xiàn)程中無(wú)法訪(fǎng)問(wèn) UI糠爬。但是主線(xiàn)程中不建議進(jìn)行耗時(shí)操作寇荧,因?yàn)檫@會(huì)引起 ANR。
系統(tǒng)為什么不允許子線(xiàn)程中訪(fǎng)問(wèn) UI执隧?
如果多線(xiàn)程并發(fā)訪(fǎng)問(wèn)揩抡,UI 控件處于不可控制的狀態(tài)户侥。如果對(duì) UI 控件的訪(fǎng)問(wèn)上鎖,首先上鎖機(jī)制會(huì)讓 UI 訪(fǎng)問(wèn)的邏輯變得復(fù)雜峦嗤;其次會(huì)降低 UI 的訪(fǎng)問(wèn)效率蕊唐,因?yàn)殒i機(jī)制會(huì)阻塞某些線(xiàn)程的執(zhí)行。所以烁设,采用單線(xiàn)程模型來(lái)處理 UI 操作簡(jiǎn)單高效替梨。
消息機(jī)制解決了我們需要從服務(wù)器獲取數(shù)據(jù)后操作 UI 的矛盾。但是更新 UI 僅僅是 Handler 的一個(gè)使用場(chǎng)景装黑。本質(zhì)上來(lái)說(shuō)副瀑,Handler 并不是專(zhuān)門(mén)用于更新 UI 的,它只是在更新 UI 的場(chǎng)景中用的比較多曹体。Handler 的使用過(guò)程很簡(jiǎn)單俗扇,通過(guò)它可以輕松的將一個(gè)任務(wù)切換到 Handler 所在的線(xiàn)程中去執(zhí)行。Handler 的運(yùn)行需要底層的 MessageQueue 和 Looper 的支撐箕别。
Android消息機(jī)制有哪些核心成員組成的呢铜幽?
- MessageQueue:
消息隊(duì)列。 它的內(nèi)部存儲(chǔ)了一組消息串稀,以隊(duì)列的形式對(duì)外提供插入和刪除的操作除抛。但是內(nèi)部存儲(chǔ)結(jié)構(gòu)并不是真正的隊(duì)列,而是采用單鏈表的數(shù)據(jù)結(jié)構(gòu)來(lái)存儲(chǔ)消息列表母截。 - Looper:
消息循環(huán)到忽。以無(wú)限循環(huán)的形式去查詢(xún)是否有新消息,如果有的話(huà)就處理消息清寇,沒(méi)有就一直等待喘漏。 - Handler:
消息傳遞的主要操作者。將消息從一個(gè)線(xiàn)程傳遞到另外一個(gè)線(xiàn)程华烟。 - ThreadLocal:
不是線(xiàn)程翩迈,它的作用是可以在每個(gè)線(xiàn)程中存儲(chǔ)數(shù)據(jù)。
Handler 創(chuàng)建的時(shí)候會(huì)采用當(dāng)前線(xiàn)程的 Looper 來(lái)構(gòu)造消息循環(huán)系統(tǒng)盔夜,Handler 內(nèi)部要使用 ThreadLocal 獲取當(dāng)前線(xiàn)程的 Looper负饲。ThreadLocal 可以在不同的線(xiàn)程中互不干擾的存儲(chǔ)并提供數(shù)據(jù),通過(guò) ThreadLocal 可以輕松獲取每個(gè)線(xiàn)程的 Looper喂链。
注意:線(xiàn)程默認(rèn)是沒(méi)有 Looper 的返十,如果需要使用 Handler 必須為線(xiàn)程創(chuàng)建 Looper。主線(xiàn)程(UI線(xiàn)程 ActivityThread)被創(chuàng)建時(shí)就會(huì)初始化 Looper椭微,這也是主線(xiàn)程中默認(rèn)可以使用 Handler 的原因洞坑。
Handler分析
Handler 是Android消息機(jī)制的上層類(lèi),也是Android消息機(jī)制中我們接觸最多的類(lèi)蝇率。使用消息機(jī)制的時(shí)候迟杂,我們都是通過(guò) Handler 的 post 和 send 系列方法開(kāi)始的匈仗,我們從這里開(kāi)始分析,借助源碼逢慌,探索Android消息機(jī)制的底層原理。我們先看 post 系列方法源碼:
public final boolean post(Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
public final boolean postAtTime(Runnable r, long uptimeMillis) {
return sendMessageAtTime(getPostMessage(r), uptimeMillis);
}
public final boolean postAtTime(Runnable r, Object token, long uptimeMillis) {
return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
}
public final boolean postDelayed(Runnable r, long delayMillis) {
return sendMessageDelayed(getPostMessage(r), delayMillis);
}
我們?cè)倏?send 系列方法的源碼:
public final boolean sendMessage(Message msg){
return sendMessageDelayed(msg, 0);
}
public final boolean sendEmptyMessage(int what){
return sendEmptyMessageDelayed(what, 0);
}
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageAtTime(msg, uptimeMillis);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(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);
}
畫(huà)個(gè)簡(jiǎn)易的圖间狂,理清這些方法之間的關(guān)系攻泼。
圖中可以發(fā)現(xiàn):post 系列和 send 系列的方法最終都指向了 sendMessageAtTime()方法。也就是說(shuō)鉴象,send 系列的方法最終都是靠 sendMessageAtTime()方法實(shí)現(xiàn)的忙菠。我們看下 sendMessageAtTime 方法的實(shí)現(xiàn):首先獲取一個(gè) MessageQueue,然后傳給 enqueueMessage 方法纺弊,調(diào)用此方法牛欢。下面是 enqueueMessage 方法源碼:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
可以看到,最后執(zhí)行的是 MessageQueue 類(lèi)的 enqueueMessage 方法淆游。我們知道 MessageQueue有兩個(gè)很重要的方法:enqueueMessage 和 next傍睹。enqueueMessage 負(fù)責(zé)往消息隊(duì)列里添加數(shù)據(jù),next 負(fù)責(zé)從消息隊(duì)列里取出數(shù)據(jù)犹菱。所以拾稳,handler post 和send 系列的方法做的事情就是往 MessageQueue 里面添加數(shù)據(jù)。
現(xiàn)在我們知道數(shù)據(jù)是怎么添加到消息隊(duì)列里面去的了腊脱,這是消息傳遞的第一步访得,那第二步就是把消息取出來(lái)進(jìn)行處理,雖然處理消息依然是 handler 的事陕凹,但把消息取出來(lái)卻是 Looper 默默一直干的事悍抑。我們分析下 Looper。
Looper
Looper 是讓我們整個(gè)消息機(jī)制循環(huán)起來(lái)的核心類(lèi)杜耙。普通的線(xiàn)程是沒(méi)有消息隊(duì)列的搜骡,也是無(wú)法使用 Handler 的(主線(xiàn)程:ActivityThread 被創(chuàng)建的時(shí)候默認(rèn)初始化 Looper ,這也是我們可以直接在主線(xiàn)程使用 Handler 的原因)泥技。正是借助 Looper 讓線(xiàn)程成為 Looper 線(xiàn)程浆兰,線(xiàn)程和 Looper 綁定后,也就有了消息隊(duì)列珊豹,因?yàn)橄㈥?duì)列 (MessageQueue) 是放在 Looper 類(lèi)里面的簸呈。那么我們?cè)趺醋隹梢宰屍胀ň€(xiàn)程變成可以使用消息機(jī)制的線(xiàn)程呢?很簡(jiǎn)單店茶,使用 Looper 類(lèi)的兩個(gè)靜態(tài)方法:
- Looper.prepare()
- Looper.loop()
這兩個(gè)方法具體做了什么呢蜕便?我們看下源碼:
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
prepare 方法很簡(jiǎn)單,里面就做了一件事贩幻,就是給變量 sThreadLocal 設(shè)置值轿腺。sThreadLocal 變量是一個(gè) ThreadLocal 類(lèi)型的變量:
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
如果對(duì) ThreadLocal 不太了解可以先看下這篇文章 Android消息機(jī)制-ThreadLocal两嘴。我們知道,ThreadLocal 的作用就是保證使用 ThreadLocal 存儲(chǔ)的數(shù)據(jù)只屬于當(dāng)前線(xiàn)程族壳,其他線(xiàn)程無(wú)法獲取憔辫。sThreadLocal 泛型是 Looper,所以這個(gè)變量會(huì)存儲(chǔ)一個(gè) Looper 對(duì)象仿荆。prepare() 方法是調(diào)用了 sThreadLocal 的 set 方法存儲(chǔ)了一個(gè)新建的 Looper 對(duì)象贰您。存儲(chǔ)之前會(huì)判斷是否已經(jīng)有了 Looper 對(duì)象,如果已經(jīng)有了拢操,會(huì)拋異常锦亦。所以,一個(gè)線(xiàn)程中只能有一個(gè) Looper 對(duì)象令境。這樣就保證了這個(gè) Looper 對(duì)象只屬于當(dāng)前線(xiàn)程杠园,而且只有一個(gè) Looper 對(duì)象。我們看看 new Looper(quitAllowed)這步都干了什么:
final MessageQueue mQueue;
final Thread mThread;
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
可以看到舔庶,首先抛蚁,構(gòu)造方法是私有的,就是在別的類(lèi)中不能實(shí)例化 Looper 對(duì)象栖茉。方法中就是給兩個(gè)變量賦值篮绿,一個(gè)是我們的消息隊(duì)列 mQueue,一個(gè)是線(xiàn)程對(duì)象 mThread吕漂。此時(shí)亲配,我們的 Looper 對(duì)象有了消息隊(duì)列,而且獲取到了當(dāng)前線(xiàn)程惶凝。然后看下 loop() 方法:
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
public static void loop() {
final Looper me = myLooper();//1吼虎、獲取looper對(duì)象
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;//2、獲取消息隊(duì)列
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) {
Message msg = queue.next(); // 取出隊(duì)列中的消息
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// This must be in a local variable, in case a UI event sets the logger
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
final long traceTag = me.mTraceTag;
if (traceTag != 0) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
try {
msg.target.dispatchMessage(msg);//消息不為空?qǐng)?zhí)行此處
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
msg.recycleUnchecked();
}
}
這里就出現(xiàn)了我們一直想尋找的東西啦苍鲜。首先 1 處通過(guò) myLooper()方法獲取到存儲(chǔ)的 looper 對(duì)象:
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
然后 2 處通過(guò)獲取到的 Looper 對(duì)象獲取到對(duì)象中的消息隊(duì)列思灰。獲取到消息隊(duì)列后,通過(guò)一個(gè)不設(shè)參數(shù)的 for 循環(huán)方法不斷取出消息混滔,如果消息不為空洒疚,就執(zhí)行:
msg.target.dispatchMessage(msg);
msg我們都知道是 Message,那么 msg.target 是什么呢坯屿?哈哈油湖,還記得我們前面分析的 Handler 把消息存入消息隊(duì)列的過(guò)程嗎,Handler 的存儲(chǔ)方法:enqueueMessage 方法:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;//這里把handler對(duì)象賦給了target
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
所以领跛,這個(gè) target 就是我們的 handler 對(duì)象乏德,next 方法取出消息后就調(diào)用了我們 handler 對(duì)象的 dispatchMessage() 方法。這里就是我們能不斷處理消息的關(guān)鍵: Looper 對(duì)象一直在幕后不斷的取出消息給我們的 handler 對(duì)象,然后由 handler 對(duì)象去處理消息喊括。
現(xiàn)在我們已經(jīng)清楚了消息隊(duì)列是什么時(shí)候構(gòu)造的胧瓜,消息是什么時(shí)候存入隊(duì)列的,消息是怎么取出的郑什,還差一步府喳,我們看看 handler 是怎么處理消息的,就是我們上面取出消息后執(zhí)行的方法:dispatchMessage
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
首先判斷 msg 的 callback 是否為空蘑拯,callback 是什么呢劫拢?是我們調(diào)用 post(Runnable r) 方法傳入的 runnable 對(duì)象:
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;
}
不為空的時(shí)候用的是 post 系列方法,如果為空則用的是 send 系列方法强胰。第二步,判斷 mCallback 是否為空妹沙,mCallback 又是什么呢偶洋?
final Callback mCallback;
public interface Callback {
public boolean handleMessage(Message msg);
}
看到這里,大家應(yīng)該明白了吧距糖,終于看到我們熟悉的處理消息的方法了:handleMessage玄窝。mCallback 是否為空對(duì)應(yīng)的是 Handler 類(lèi)的不同構(gòu)造方法。
public Handler() {
this(null, false);
}
public Handler(Callback callback) {
this(callback, false);
}
我們?cè)趧?chuàng)建 handler 對(duì)象的時(shí)候悍引,可以直接傳入一個(gè)匿名內(nèi)部類(lèi)去實(shí)現(xiàn) handleMessage 方法恩脂;也可以構(gòu)造方法不傳參,然后去實(shí)現(xiàn) handlerMessage 方法趣斤。所以俩块,在 dispatchMessage 方法中,判斷如果 mCallback 為空的話(huà)浓领,執(zhí)行 handleMessage 方法玉凯。這樣,我們就走到了 handleMessage 方法联贩,就可以按照我們的業(yè)務(wù)邏輯去處理消息了漫仆。
最后畫(huà)一張圖總結(jié)一下整個(gè)流程: