1.前言
android消息處理機制很基礎(chǔ),也很重要。一般耗時操作需要放到子線程中去執(zhí)行,執(zhí)行完后,需要把結(jié)果反饋給主線程,在主線程中更新ui。比如:在子線程中加載一張網(wǎng)絡(luò)圖片,加載成功后,在主線程中顯示圖片。那么,子線程怎樣把結(jié)果反饋給主線程?這就用到了我們今天講到的消息處理。
2.簡要說明消息處理過程
消息處理機制主要涉及四個類:Looper,Message,MessageQueue,Handler。過程如下:
一個線程有且只有一個Looper對象,Looper是循環(huán)的意思,Looper對象創(chuàng)建自己的消息隊列MessageQueue,當(dāng)有消息message產(chǎn)生時,message會被壓入隊列message queue。Looper一直循環(huán)做如下工作:從message queue中取出一個 msg,handler來處理。借用一張圖:

具體怎么處理下面會詳細(xì)說明。
3. Looper
先來說說Looper。
Looper主要作用:
與當(dāng)前線程綁定,保證一個線程只會有一個Looper實例,同時一個Looper實例也只有一個MessageQueue(創(chuàng)建消息隊列)。
loop()方法不斷從MessageQueue中去取消息,交給消息
Message的target屬性的dispatchMessage()去處理。
Looper中的變量:
// 每個線程中的Looper對象其實是一個ThreadLocal,即線程本地存儲(TLS)對象
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
final MessageQueue mQueue;//looper中的消息隊列
final Thread mThread;//綁定的線程
Looper通過prepare()方法來創(chuàng)建looper對象:
//quitAllowed:消息循環(huán)是否可以退出(主線程傳進(jìn)去的是false)
private static void prepare(boolean quitAllowed) {
//如果一個線程中已經(jīng)有l(wèi)ooper對象,再創(chuàng)建就會拋出異常,也就是說,一個線程只能有一個looper對象
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
調(diào)用這個方法之后,需調(diào)用loop()方法來從消息隊列中取出消息:
public static void loop() {
//得到當(dāng)前線程的Looper實例
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();
//looper開始無限循環(huán)
for (;;) {
Message msg = queue.next(); // 取出對列頭的message(也可以叫task)
if (msg == null) {
//如果消息隊列中沒有task,就退出循環(huán)
return;
}
//日志
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
//使用調(diào)用 msg.target.dispatchMessage(msg);把消息交給msg的target的dispatchMessage方法去處理。
// Msg的target是什么呢?其實就是handler對象,下面會進(jìn)行分析。
msg.target.dispatchMessage(msg);
//日志
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
/*
clearCallingIdentity這個可以看成是安全性代碼,也可以看成是調(diào)試代碼
作用是確定當(dāng)前這個looper所在的“線程”是否一直在同一個“進(jìn)程”里,如果進(jìn)程變多半是說明這個線程運行在某種跨進(jìn)程代碼里。
比如說你通過AIDL調(diào)用stub,遠(yuǎn)程那邊接到之后啟動一個線程,就有可能觸發(fā)ident != newIdent了
*/
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);
}
//釋放消息占據(jù)的資源
msg.recycleUnchecked();
}
}
public static MessageQueue myQueue() {
return myLooper().mQueue;
}
/*
* 得到當(dāng)前線程的Looper實例
* */
public static Looper myLooper() {
return sThreadLocal.get();
}
如果要結(jié)束loop()循環(huán),可以調(diào)用quit()退出循環(huán)。
public void quit() {
mQueue.quit(false);
}
注意:
looper方法必須在prepare方法之后運行
每個線程有且最多只能有一個Looper對象,它是一個ThreadLocal;一個消息隊列(如果一個線程已經(jīng)有l(wèi)ooper對象,再創(chuàng)建會拋異常)
Looper內(nèi)部有一個消息隊列,loop()方法調(diào)用后線程開始不斷從隊列中取出消息執(zhí)行
Looper使一個線程變成Looper線程。
另外,Looper中還有一些方法看一下:
//獲取message queue
public static MessageQueue myQueue() {
return myLooper().mQueue;
}
/*
* 得到當(dāng)前線程的Looper實例
* */
public static Looper myLooper() {
return sThreadLocal.get();
}
/*
* 給主線程初始化一個MainLooper,MainLooper一般是系統(tǒng)create,所以自己永遠(yuǎn)不用調(diào)用,知道有這么個東西就好了。
* */
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
/*
* 得到主線程的Looper實例
* */
public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
}
4.Message
上面講到,從looper中取出一個msg后,通過這句代碼交給msg的target來處理消息:msg.target.dispatchMessage(msg)。
target是什么?
我們來看看Message類有什么東西。先看看有哪些變量:
Handler target;//target是處理消息的Handler對象
Runnable callback;//回調(diào)callback
private static Message sPool;
private static int sPoolSize = 0; /
//message內(nèi)容
public int what;
Bundle data;
public Object obj;
public int arg1;
public int arg2;
獲取Message實例可以直接new 一個Message,也可以通過調(diào)用Message的obtain()方法,Message中obtain()方法有很多,這里只看一個。
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();
}
所以,Message類中有一個Handler類型的變量target,來處理消息。真正處理消息的,是Handler類。下面我們來看Handler類。
注意:
產(chǎn)生一個Message對象,可以new,也可以使用Message.obtain()方法;
兩者都可以,但是更建議使用obtain方法,因為Message內(nèi)部維護(hù)了一個Message池用于Message的復(fù)用,避免使用new 重新分配內(nèi)存。
5.Handler
依舊先看看變量:
final MessageQueue mQueue;//handler所在的線程的消息隊列
final Looper mLooper;//handler所在線程的looper對象
//這個callback是一個接口,create handler對象的時候需要實現(xiàn)它。里面有一個handlerMessage()方法,
//handlerMessage()方法就是我們需要自己實現(xiàn)的處理消息的方法。
final Callback mCallback;
CallBack接口:
public interface Callback {
public boolean handleMessage(Message msg);
}
我們通常這樣用:
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
break;
}
}
};
熟悉吧?這個handlerMessage()就是Handler類中的interface mCallback 里的handlerMessage()方法。
前面說過,looper從消息隊列中取出一個message,通過message的target.dispatchMessage(msg)(Handler)來處理該消息。那么,Handler是如何與MessageQueue聯(lián)系上的,它在子線程中發(fā)送的消息(一般發(fā)送消息都在非UI線程)又是怎么發(fā)送到MessageQueue中的?
我們來看看Handler的構(gòu)造方法:
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());
}
}
//得到當(dāng)前線程中的looper對象
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;
}
ok,上面疑問搞清楚了。我們接著來看之前一直說的dispatchMessage(msg)方法。
/*
* 這個方法處理消息
* 如果msg的callback和target(handler)都有值,會執(zhí)行哪個?
* 通過getPostMessage(Runnerable r)方法可以知道,r即指定msg的callback。
* */
public void dispatchMessage(Message msg) {
//先執(zhí)行msg的callback
if (msg.callback != null) {
handleCallback(msg);
} else {
//如果msg的callback為空,則執(zhí)行msg的handler對象target的回調(diào)mCallback。
// 這個mCallback就是我們平時創(chuàng)建handler時實現(xiàn)的接口handleressage()。
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
好了,到這里,消息處理就說完了。handler類里有一些發(fā)送消息的方法,post(Runnable r),sendMessageDelayed(Message msg, long delayMillis),sendMessageAtTime(Message msg, long uptimeMillis),postAtTime(Runnable r, long uptimeMillis)等等,它們的作用都是把一個msg壓入message queue中等待處理。如果想知道 post和sendMessage的區(qū)別,怎么將msg壓入message queue,這些方法的具體實現(xiàn)等,可以看我的博客 postDelayed, sendMessageAtTime等handler發(fā)送消息方法總結(jié)。
參考資料: