關(guān)于Android中的異步消息處理機(jī)制爸舒,平時(shí)在項(xiàng)目中應(yīng)該算是用的很多了蟋字。最近看了一些這方面的源碼,記錄一下扭勉。
首先鹊奖,來(lái)看一下平時(shí)是怎么用的吧。最常見(jiàn)的使用場(chǎng)景涂炎,可能就是在子線程中處理一些耗時(shí)操作忠聚,然后更新UI了设哗。那么,這是怎么做的呢两蟀?
在子線程中處理耗時(shí)操作网梢,然后新建了一個(gè)Message(這個(gè)Message里面會(huì)包含很多內(nèi)容,一會(huì)兒再細(xì)說(shuō))赂毯,通過(guò)一個(gè)Handler將Message發(fā)送出去战虏。
new Thread(new Runnable() {
@Override
public void run() {
// 執(zhí)行耗時(shí)操作
// 發(fā)送消息
Message message = Message.obtain();
message.what = 1;
message.arg1 = 2;
handler.sendMessage(message);
}
}).start();
在主線程中,我們有一個(gè)Handler党涕,并且重寫(xiě)了handleMessage方法活烙,用來(lái)接收并處理消息。
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
if (msg.what == 1) {
// 更新UI
}
}
};
其實(shí)我們常見(jiàn)的就是Handler和Message這兩個(gè)東西遣鼓,那么就先來(lái)看一下他們到底是什么吧。
Handler
A Handler allows you to send and process Message and Runnable objects associated with a thread's MessageQueue. Each Handler instance is associated with a single thread and that thread's message queue. When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it -- from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.
There are two main uses for a Handler: (1) to schedule messages and runnables to be executed as some point in the future; and (2) to enqueue an action to be performed on a different thread than your own.
這個(gè)就是官方對(duì)Handler的定義重贺,簡(jiǎn)單來(lái)說(shuō)骑祟,Handler就是用來(lái)發(fā)送和處理Message的,而且很重要的一點(diǎn)气笙,每個(gè)Handler的實(shí)例都關(guān)聯(lián)了一個(gè)線程和這個(gè)線程的MessageQueue次企。
Message
Defines a message containing a description and arbitrary data object that can be sent to a Handler. This object contains two extra int fields and an extra object field that allow you to not do allocations in many cases.
什么是Message?Message就是一個(gè)包含了描述和任意數(shù)據(jù)的對(duì)象潜圃,可以被發(fā)送給Handler進(jìn)行處理缸棵。
While the constructor of Message is public, the best way to get one of these is to call Message.obtain() or one of the Handler.obtainMessage() methods, which will pull them from a pool of recycled objects.
官方在這里明確表示,雖然Message的構(gòu)造函數(shù)是公有的谭期,但是最好的方式是通過(guò)Message.obtain()
或者Handler.obtainMessage()
之類(lèi)的方法來(lái)創(chuàng)建Message堵第,因?yàn)檫@樣會(huì)從一個(gè)全局的池中來(lái)復(fù)用Message,而不是每次都新建一個(gè)隧出。
/**
* Return a new Message instance from the global pool. Allows us to
* avoid allocating new objects in many cases.
*/
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
MessageQueue
Low-level class holding the list of messages to be dispatched by a Looper. Messages are not added directly to a MessageQueue, but rather through Handler objects associated with the Looper.
前面說(shuō)了每個(gè)Handler的實(shí)例都關(guān)聯(lián)了一個(gè)MessageQueue踏志,那么MessageQueue是什么?其實(shí)從名字就能很容易的看出胀瞪,MessageQueue就是存儲(chǔ)Message的一個(gè)隊(duì)列针余,先進(jìn)先出。那么MessageQueue是在哪呢凄诞?接下來(lái)看另一個(gè)很重要的東西Looper圆雁。
Looper
Class used to run a message loop for a thread. Threads by default do not have a message loop associated with them; to create one, call prepare() in the thread that is to run the loop, and then loop() to have it process messages until the loop is stopped.
其實(shí)Looper就是一個(gè)無(wú)限循環(huán)的任務(wù),他不斷的從MessageQueue中嘗試取出Message帆谍,如果沒(méi)有取到就繼續(xù)嘗試伪朽,如果取到了就交給Hander去處理,直到這個(gè)循環(huán)被停止既忆。
好了驱负,說(shuō)了這么多概念嗦玖,讓我們來(lái)看看源碼。還是從Handler開(kāi)始跃脊,畢竟是直接和我們打交道的宇挫。
無(wú)論使用哪種方式創(chuàng)建Handler,最后其實(shí)都離不開(kāi)這兩個(gè)構(gòu)造函數(shù)酪术。
1 使用當(dāng)前線程默認(rèn)的Looper
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
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;
}
2 使用指定的Looper而不是默認(rèn)的
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
區(qū)別就在于是使用當(dāng)前線程的默認(rèn)Looper還是使用指定的Looper了器瘪。這里我們可以看到,如果使用的是默認(rèn)Looper绘雁,會(huì)調(diào)用Looper.myLooper()
方法橡疼。
讓我們來(lái)看看Handler中有哪些常用的方法,基本可以分為兩大類(lèi)post(Runnable r)
和sendMessage(Message msg)
庐舟,而他們的區(qū)別在于post類(lèi)的方法會(huì)調(diào)用下面這個(gè)方法欣除,指定Message的callback。
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
然后返回一個(gè)Message挪略,最終基本上都會(huì)調(diào)用到這個(gè)方法
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);
}
其中MessageQueue queue = mQueue;
指定了Handler對(duì)應(yīng)的MessageQueue历帚,然后調(diào)用入隊(duì)列的方法
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
其中msg.target = this
是將Message和Handler綁定在了一起,而queue.enqueueMessage(msg, uptimeMillis)
就是將Message入到隊(duì)列MessageQueue中去杠娱。
Message就是我們需要發(fā)送和處理的消息挽牢,其中大概包含了這么幾個(gè)我們需要用到的東西。
- public int what
我們自定義的一個(gè)類(lèi)似編號(hào)的東西摊求,可以用來(lái)與其他Message進(jìn)行區(qū)分 - public int arg1禽拔,arg2
可以存一些輕量級(jí)的數(shù)據(jù) - public Object obj
可以存對(duì)象 - Bundle data
用來(lái)存儲(chǔ)復(fù)雜的數(shù)據(jù) - Handler target
與Message關(guān)聯(lián)的Handler,也就是負(fù)責(zé)處理消息的Handler - Runnable callback
可以指定運(yùn)行在某個(gè)Runnable上
再來(lái)看一下Looper室叉。
- 構(gòu)造方法
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
Looper的構(gòu)造方法中只做了兩件事:
1睹栖、生成了一個(gè)MessageQueue,這里也就明白了前面說(shuō)的保存Message的這個(gè)隊(duì)列MessageQueue在哪里了茧痕,沒(méi)錯(cuò)磨淌,就是在Looper里。
2凿渊、得到當(dāng)前所在的線程梁只。
構(gòu)造方法做的這兩件事很重要,說(shuō)明了每一個(gè)Looper中都包含了一個(gè)MessageQueue埃脏,并且關(guān)聯(lián)了一個(gè)特定的線程搪锣,這也是異步消息處理的關(guān)鍵。
- prepare()方法
/** Initialize the current thread as a looper.
* This gives you a chance to create handlers that then reference
* this looper, before actually starting the loop. Be sure to call
* {@link #loop()} after calling this method, and end it by calling
* {@link #quit()}.
*/
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方法會(huì)檢查是否已經(jīng)有Looper存在彩掐,如果存在會(huì)拋出異常构舟,因?yàn)橐粋€(gè)線程中只能有一個(gè)Looper存在,如果不存在則創(chuàng)建一個(gè)新的Looper堵幽,存在一個(gè)ThreadLocal里狗超。
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
我看到網(wǎng)上很多文章在講Handler機(jī)制時(shí)并沒(méi)有過(guò)多的提到ThreadLocal弹澎,但是其實(shí)我覺(jué)得這也是很重要的一部分。到底什么是ThreadLocal?簡(jiǎn)單來(lái)說(shuō)就是將對(duì)象在不同線程中分別保存了不同的副本,在某一個(gè)線程中改變了對(duì)象的值時(shí)蕊爵,并不會(huì)影響其他線程中的副本冶忱。所以這里用來(lái)保存Looper纵东,不同線程中的MessageQueue將會(huì)互不影響。
如果我們?cè)谧泳€程中創(chuàng)建Handler之前不調(diào)用prepare方法,將會(huì)拋出異常,但是為什么我們?cè)谥骶€程中不需要調(diào)用prepare方法呢报强?那是因?yàn)橄到y(tǒng)會(huì)自動(dòng)調(diào)用prepareMainLooper方法為我們創(chuàng)建主線程的Looper。
/**
* Initialize the current thread as a looper, marking it as an
* application's main looper. The main looper for your application
* is created by the Android environment, so you should never need
* to call this function yourself. See also: {@link #prepare()}
*/
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
而前面在Handler的構(gòu)造函數(shù)中的myLooper方法拱燃,只是從ThreadLocal中取出Looper而已秉溉。
/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
- loop方法
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) {
Message msg = queue.next(); // might block
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.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
try {
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
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();
}
}
looper方法就是一個(gè)無(wú)限循環(huán)的任務(wù),不斷的從MessageQueue中取出Message進(jìn)行處理碗誉。其中final MessageQueue queue = me.mQueue;
就是從當(dāng)前Looper中取出了MessageQueue坚嗜,而msg.target.dispatchMessage(msg);
就是真正處理消息的地方,其中msg.target就是與Message綁定在一起的那個(gè)Handler诗充。
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
callback是什么?callback就是通過(guò)post類(lèi)的方法指定的Runnable诱建,如果callback不為空就執(zhí)行
private static void handleCallback(Message message) {
message.callback.run();
}
否則就執(zhí)行handleMessage(msg);
看到這里不難發(fā)現(xiàn)蝴蜓,就像官方的定義那樣(廢話,當(dāng)然一樣俺猿。茎匠。。)押袍,每個(gè)Handler都對(duì)應(yīng)了一個(gè)Looper和一個(gè)MessageQueue诵冒,關(guān)聯(lián)了一個(gè)Thread,所以才能實(shí)現(xiàn)異步消息處理谊惭。比如我們?cè)谥骶€程上創(chuàng)建了一個(gè)Handler汽馋,就創(chuàng)建了一個(gè)Looper和MessageQueue,關(guān)聯(lián)的就是主線程圈盔,然后在子線程中發(fā)出消息豹芯,這個(gè)消息就會(huì)存儲(chǔ)在主線程上的MessageQueue里,并且由Handler進(jìn)行處理驱敲。這樣就完成了在子線程中發(fā)送消息铁蹈,在主線程中處理的過(guò)程,當(dāng)然這只是其中的一種應(yīng)用場(chǎng)景众眨。