一、基本概念:
在談到Android消息機制的時候,我們不可避免的要涉及要一下幾個概念:
1、Message 消息
2庸娱、MessageQueue 消息隊列
3、Handler 處理者谐算?它的實際作用包括發(fā)送消息(sendMessage),處理消息(handleMessage)
4归露、Looper 消息循環(huán)洲脂,或者輪詢器
從字面上來看,前兩者都比較好理解剧包。剩下的Handler恐锦、Looper,讓我們先重點理解一下Looper:
二疆液、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.
簡單翻譯過來就是:Looper用于管理一個線程的消息循環(huán)一铅。 默認(rèn)情況下,線程是沒有相關(guān)聯(lián)的Looper的堕油,需要調(diào)用Looper.prepare()來創(chuàng)建潘飘,創(chuàng)建完成之后肮之,Looper.loop()會不斷地循環(huán)來處理消息,直到循環(huán)停止卜录。一般情況下是和Handler來配合使用的戈擒。
使用Looper+Handler典型代碼例如:
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare(); //為本線程創(chuàng)建一個Looper
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop(); //消息循環(huán)
}
}
Looper重要屬性:
// sThreadLocal.get() will return null unless you've called prepare()
static final ThreadLocal sThreadLocal = new ThreadLocal();
private static Looper sMainLooper; // guarded by Looper.class
final MessageQueue mQueue;
final Thread mThread;
其中,sThreadLocal是一個ThreadLocal對象艰毒,可以在一個線程中存儲變量筐高。
Looper的prepare()方法:
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) { //一個線程只能對應(yīng)一個Looper實例
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed)); //new一個Looper的實例保存在ThreadLocal中
}
可以看到,prepare()方法將new一個Looper的實例丑瞧,同時將該Looper實例放入了ThreadLocal柑土。并且當(dāng)?shù)诙握{(diào)用時報錯,說明prepare()方法不能被調(diào)用兩次绊汹,同時也保證了一個線程中只有一個Looper實例稽屏。
Looper的構(gòu)造函數(shù):
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
Looper構(gòu)造函數(shù)中創(chuàng)建一個消息隊列MessageQueue。同時也可以看到灸促,一個Looper實例對應(yīng)一個線程诫欠、一個MessageQueue
Looper的loop()方法:
public static void loop() {
// myLooper() 返回的是sThreadLocal.get(),也就是sThreadLocal存儲的Looper實例
final Looper me = myLooper();
if (me == null) {
// 可以看到這里浴栽,如果在線程中沒有調(diào)用Looper.prepare()的話Looper實例是空的
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue; //拿到Looper實例對應(yīng)的消息隊列
// 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 (;;) { //不斷地從消息隊列中取消息來進(jìn)行處理
Message msg = queue.next(); // might block
if (msg == null) { // No message indicates that the message queue is quitting.
return;
}
... ...
msg.target.dispatchMessage(msg); //真正處理消息的地方
... ...
// 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();
}
}
可以看到荒叼,方法中無限循環(huán)體里面,不斷地通過queue.next()來取出準(zhǔn)備處理的消息典鸡。而真正處理消息的地方是在這一句:msg.target.dispatchMessage(msg)被廓。target是什么?我們可以從Message源碼中可以看到萝玷,target是一個Handler嫁乘,也就是說,真正處理消息是該消息對應(yīng)的Handler實例的dispatchMessage(msg)方法中球碉。
小結(jié):
1蜓斧、Looper用于管理一個線程的消息循環(huán)。
2睁冬、由于默認(rèn)情況下挎春,線程是沒有相關(guān)聯(lián)的Looper的,因此必須調(diào)用Looper.prepare()來創(chuàng)建豆拨。
3直奋、Looper.prepare()方法,會使得線程綁定唯一一個Looper實例
4施禾、Looper創(chuàng)建時也會創(chuàng)建一個消息隊列MessageQueue脚线。
2、Looper.loop()方法弥搞,不斷從MessageQueue.next()中去取出準(zhǔn)備處理的消息邮绿,交給消息的對應(yīng)的handler的dispatchMessage去處理渠旁。
簡言之,Looper提供了存儲消息的隊列(MessageQueue)斯碌,同時不斷循環(huán)取出準(zhǔn)備處理的消息一死。那么誰來發(fā)送消息和真正處理消息呢?——當(dāng)然是我們熟悉的Handler了傻唾。
三投慈、Handler:
從第一部分我們可以推測出,Handler的兩個重要作用:
1冠骄、發(fā)消息到MessageQueue中
2伪煤、接受Looper分發(fā)的消息處理任務(wù),真正處理消息
那么Handler是怎么和一個線程的MessageQueue凛辣、Looper實例關(guān)聯(lián)上的呢抱既?
這個要看一下Handler是怎樣被new出來的倦微。具體我會另開文分析辛辨,這里以開篇的例子來分析,當(dāng)使用new Handler()來創(chuàng)建一個Handler的情況(意思是還有其他情況):
public Handler() {
this(null, false);
}
最后是調(diào)用了這個:
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class 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(); //這里闽烙,關(guān)聯(lián)上了當(dāng)前線程的Looper實例
if (mLooper == null) {
throw new RuntimeException(
// 在一個線程里面蝗敢,創(chuàng)建handler之前必須調(diào)用Looper.prepare()
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue; // 這里捷泞,取得當(dāng)前線程的Looper實例的消息隊列
mCallback = callback;
mAsynchronous = async;
}
可以看到,在這種情況下寿谴,在構(gòu)造函數(shù)里面锁右,通過Looper.myLooper()獲取了當(dāng)前線程保存的Looper實例,然后又獲取了這個Looper實例中保存的消息隊列MessageQueue讶泰。Handler實例就是這樣和一個線程的MessageQueue咏瑟、Looper實例一一關(guān)聯(lián)上的。
這里的當(dāng)前線程痪署,是指創(chuàng)建Handler實例所使用的線程码泞。
接下來就簡單了,用Handler發(fā)送一條消息狼犯,不論使用何種方法余寥,最終都會調(diào)到這個函數(shù):
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue; //當(dāng)前線程對應(yīng)Looper實例的消息隊列
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);
}
然后調(diào)用了這個函數(shù):
private boolean enqueueMessage(MessageQueue queue, Message msg,long uptimeMillis) {
msg.target =this; //將消息的target設(shè)為當(dāng)前的Handler實例
if(mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis); //將消息插入到消息隊列中去
}
重要的是這句:msg.target = this; //將消息的target設(shè)為當(dāng)前的Handler實例
還記得第一部分結(jié)尾處,loop()方法中取出一條要處理的消息辜王,然后調(diào)用msg.target.dispatchMessage(msg)嘛?那么現(xiàn)在我們知道了罐孝,msg的target就是在發(fā)這條消息的時候設(shè)置上的呐馆。以保證后續(xù)處理消息的時候,能找到處理這條消息的Handler莲兢。
queue.enqueueMessage(msg, uptimeMillis); //這句將設(shè)置好target等屬性的消息放到消息隊列中去
終于汹来,讓我們看一下msg.target.dispatchMessage(msg)的dispatchMessage()函數(shù):
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
public void handleMessage(Message msg) {
}
這個方法最終調(diào)用我們在創(chuàng)建Handler時重寫的handleMessage()方法或者callback來進(jìn)行消息處理续膳;
而handleMessage()我們就很熟悉了,每個創(chuàng)建Handler的都要重寫handleMessage()方法收班,根據(jù)msg.what來處理消息坟岔,例如:
private Handler mHandler = newHandler() {
public void handleMessage(android.os.Message msg) {
switch(msg.what) {
case:
break;
default:
break;
}
};
};
三、總結(jié)
1摔桦、默認(rèn)情況下線程是沒有Looper實例的社付,Looper.prepare()為線程關(guān)聯(lián)到唯一的Looper實例,以及Looper實例的MessageQueue邻耕。
2鸥咖、Handler被創(chuàng)建時,會關(guān)聯(lián)到“某個線程”的唯一的Looper實例和MessageQueue兄世;Handler的主要作用是將處理消息切換到“這個線程”來處理啼辣。
3、當(dāng)通過Handler發(fā)送一條Message時御滩,該消息的target就被設(shè)為這個Handler實例(this)
4鸥拧、Handler發(fā)送一條消息,實際上是將這條消息插入到對應(yīng)的消息隊列MessageQueue中
5削解、線程對應(yīng)的Looper實例loop()方法來處理消息時富弦,會根據(jù)這條消息的target(也就是Handler實例),回調(diào)Handler的dispatchMessage()方法進(jìn)行處理钠绍。dispatchMessage()方法最終調(diào)用我們在創(chuàng)建Handler時重寫的handleMessage()方法或者callback舆声。
這里面,比較重要的是柳爽,明確Handler被創(chuàng)建時媳握,和哪個線程或者和哪個線程的Looper相關(guān)聯(lián),這將決定了任務(wù)最后在哪個線程執(zhí)行磷脯;
這里面有個特殊情況蛾找,就是在主線程創(chuàng)建Handler時不需要調(diào)用prepare()和loop(),因為這部分工作Android已經(jīng)幫我們做過了赵誓,具體可以看一下ActivityThread的代碼打毛。
簡單的說,一條消息從發(fā)出到處理經(jīng)歷了:
[Handler發(fā)送消息] -> [消息進(jìn)入Looper的MessageQueue隊列] -> [loop循環(huán)從MessageQueue取出要處理的消息] -> [Handler處理消息]
Reference:
Android 異步消息處理機制 讓你深入理解 Looper俩功、Handler幻枉、Message三者關(guān)系
Android異步消息處理機制完全解析,帶你從源碼的角度徹底理解