一、基本概念:
在談到Android消息機制的時候,我們不可避免的要涉及要一下幾個概念:
1、Message 消息
2、MessageQueue 消息隊列
3、Handler 處理者?它的實際作用包括發送消息(sendMessage),處理消息(handleMessage)
4、Looper 消息循環,或者輪詢器
從字面上來看,前兩者都比較好理解。剩下的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用于管理一個線程的消息循環。 默認情況下,線程是沒有相關聯的Looper的,需要調用Looper.prepare()來創建,創建完成之后,Looper.loop()會不斷地循環來處理消息,直到循環停止。一般情況下是和Handler來配合使用的。
使用Looper+Handler典型代碼例如:
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare(); //為本線程創建一個Looper
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop(); //消息循環
}
}
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) { //一個線程只能對應一個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。并且當第二次調用時報錯,說明prepare()方法不能被調用兩次,同時也保證了一個線程中只有一個Looper實例。
Looper的構造函數:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
Looper構造函數中創建一個消息隊列MessageQueue。同時也可以看到,一個Looper實例對應一個線程、一個MessageQueue
Looper的loop()方法:
public static void loop() {
// myLooper() 返回的是sThreadLocal.get(),也就是sThreadLocal存儲的Looper實例
final Looper me = myLooper();
if (me == null) {
// 可以看到這里,如果在線程中沒有調用Looper.prepare()的話Looper實例是空的
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue; //拿到Looper實例對應的消息隊列
// 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;
}
... ...
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();
}
}
可以看到,方法中無限循環體里面,不斷地通過queue.next()來取出準備處理的消息。而真正處理消息的地方是在這一句:msg.target.dispatchMessage(msg)。target是什么?我們可以從Message源碼中可以看到,target是一個Handler,也就是說,真正處理消息是該消息對應的Handler實例的dispatchMessage(msg)方法中。
小結:
1、Looper用于管理一個線程的消息循環。
2、由于默認情況下,線程是沒有相關聯的Looper的,因此必須調用Looper.prepare()來創建。
3、Looper.prepare()方法,會使得線程綁定唯一一個Looper實例
4、Looper創建時也會創建一個消息隊列MessageQueue。
2、Looper.loop()方法,不斷從MessageQueue.next()中去取出準備處理的消息,交給消息的對應的handler的dispatchMessage去處理。
簡言之,Looper提供了存儲消息的隊列(MessageQueue),同時不斷循環取出準備處理的消息。那么誰來發送消息和真正處理消息呢?——當然是我們熟悉的Handler了。
三、Handler:
從第一部分我們可以推測出,Handler的兩個重要作用:
1、發消息到MessageQueue中
2、接受Looper分發的消息處理任務,真正處理消息
那么Handler是怎么和一個線程的MessageQueue、Looper實例關聯上的呢?
這個要看一下Handler是怎樣被new出來的。具體我會另開文分析,這里以開篇的例子來分析,當使用new Handler()來創建一個Handler的情況(意思是還有其他情況):
public Handler() {
this(null, false);
}
最后是調用了這個:
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(); //這里,關聯上了當前線程的Looper實例
if (mLooper == null) {
throw new RuntimeException(
// 在一個線程里面,創建handler之前必須調用Looper.prepare()
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue; // 這里,取得當前線程的Looper實例的消息隊列
mCallback = callback;
mAsynchronous = async;
}
可以看到,在這種情況下,在構造函數里面,通過Looper.myLooper()獲取了當前線程保存的Looper實例,然后又獲取了這個Looper實例中保存的消息隊列MessageQueue。Handler實例就是這樣和一個線程的MessageQueue、Looper實例一一關聯上的。
這里的當前線程,是指創建Handler實例所使用的線程。
接下來就簡單了,用Handler發送一條消息,不論使用何種方法,最終都會調到這個函數:
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue; //當前線程對應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);
}
然后調用了這個函數:
private boolean enqueueMessage(MessageQueue queue, Message msg,long uptimeMillis) {
msg.target =this; //將消息的target設為當前的Handler實例
if(mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis); //將消息插入到消息隊列中去
}
重要的是這句:msg.target = this; //將消息的target設為當前的Handler實例
還記得第一部分結尾處,loop()方法中取出一條要處理的消息,然后調用msg.target.dispatchMessage(msg)嘛?那么現在我們知道了,msg的target就是在發這條消息的時候設置上的。以保證后續處理消息的時候,能找到處理這條消息的Handler。
queue.enqueueMessage(msg, uptimeMillis); //這句將設置好target等屬性的消息放到消息隊列中去
終于,讓我們看一下msg.target.dispatchMessage(msg)的dispatchMessage()函數:
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) {
}
這個方法最終調用我們在創建Handler時重寫的handleMessage()方法或者callback來進行消息處理;
而handleMessage()我們就很熟悉了,每個創建Handler的都要重寫handleMessage()方法,根據msg.what來處理消息,例如:
private Handler mHandler = newHandler() {
public void handleMessage(android.os.Message msg) {
switch(msg.what) {
case:
break;
default:
break;
}
};
};
三、總結
1、默認情況下線程是沒有Looper實例的,Looper.prepare()為線程關聯到唯一的Looper實例,以及Looper實例的MessageQueue。
2、Handler被創建時,會關聯到“某個線程”的唯一的Looper實例和MessageQueue;Handler的主要作用是將處理消息切換到“這個線程”來處理。
3、當通過Handler發送一條Message時,該消息的target就被設為這個Handler實例(this)
4、Handler發送一條消息,實際上是將這條消息插入到對應的消息隊列MessageQueue中
5、線程對應的Looper實例loop()方法來處理消息時,會根據這條消息的target(也就是Handler實例),回調Handler的dispatchMessage()方法進行處理。dispatchMessage()方法最終調用我們在創建Handler時重寫的handleMessage()方法或者callback。
這里面,比較重要的是,明確Handler被創建時,和哪個線程或者和哪個線程的Looper相關聯,這將決定了任務最后在哪個線程執行;
這里面有個特殊情況,就是在主線程創建Handler時不需要調用prepare()和loop(),因為這部分工作Android已經幫我們做過了,具體可以看一下ActivityThread的代碼。
簡單的說,一條消息從發出到處理經歷了:
[Handler發送消息] -> [消息進入Looper的MessageQueue隊列] -> [loop循環從MessageQueue取出要處理的消息] -> [Handler處理消息]
Reference:
Android 異步消息處理機制 讓你深入理解 Looper、Handler、Message三者關系
Android異步消息處理機制完全解析,帶你從源碼的角度徹底理解