android的消息機制就是指Handler、MessageQueue和Looper的工作過程。
需要傳遞消息時,由Handler會調用MessageQueue的enqueueMessage方法將消息放入消息隊列中,Looper會不斷從隊列裏取消息,取出以後再交給Handler去處理。
Handler在創建時要使用當前線程的Looper來構建內部的消息處理系統,所以如果當前線程沒有Looper,就會報錯:
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
我們在主線程(ActivityThread)裏使用Handler時通常都是直接使用,並沒有創建Looper,那是因爲系統會通過Looper.prepareMainLooper()來創建主線程的Looper,所以主線程中默認可以直接使用Handler。
Looper的工作原理
Looper的構造方法裏會新建MessageQueue並保存當前的線程信息,所以MessageQueue是由Looper去創建並維護的。
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
如何去創建一個Looper?
Looper.prepare();
上述代碼只是在當前線程裏創建了Looper對象,要想使其工作(可以不斷的從消息隊列中取消息並處理消息)還需要調用:
Looper.loop();
來看一下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
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
msg.target.dispatchMessage(msg);
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();
}
}
通過源碼可知,loop方法是一個死循環,不斷的調用queue.next()去從消息隊列中取消息。當消息爲null時纔會返回。需要注意的是,如果當前消息隊列裏沒有消息,queue.next()方法會阻塞住而並不是返回null。那麼什麼時候纔是null退出循環呢?只有調用Looper.quit()或Looper.quitSafely()方法,這樣就會調用MessageQueue的quit/quitSafely來通知消息隊列退出,這時queue.next()就會返回null了。
quit和quitSafely方法的區別是:quit會直接退出Looper,而quitSafely會等待當前消息隊列中已有的消息全部處理完畢後才完全退出。
在子線程中,如果手動創建了Looper,那麼當所有事情完成以後應該調用quit方法來終止消息循環,否則子線程會一直處於等待的狀態。而當Looper退出後,Handler的send方法會返回false,線程會立刻終止,因此建議不需要的時候終止Looper。
通過queue.next()取得消息後,Looper會調用msg.target.dispatchMessage(msg); 這裏的msg.target就是指Handler,這樣發送的消息又交給它的dispatchMessage方法來處理了。
Handler的工作原理
我們經常使用Handler的sendMessage或sendMessageDelayed的方法,它們最終都會調到enqueueMessage()來往消息隊列中插入消息。然後消息交給Looper處理,Looper又調用Handler的dispatchMessage方法。dispatchMessage的源碼如下所示:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
通過源碼可知,會首先檢查Message的callback是否爲null。Message的callback實際上就是Handler的post方法所傳遞的Runnable參數,這裏調用的handleCallback(msg)就是要去執行Runnable的run方法:
private static void handleCallback(Message message) {
message.callback.run();
}
其次會檢查mCallback是否爲null,這個Callback是指:
/**
* Callback interface you can use when instantiating a Handler to avoid
* having to implement your own subclass of Handler.
*
* @param msg A {@link android.os.Message Message} object
* @return True if no further handling is desired
*/
public interface Callback {
public boolean handleMessage(Message msg);
}
當我們創建Handler對象時,如果是用這個構造方法,就相當於爲mCallback賦值了:
public Handler(Callback callback) {
this(callback, false);
}
我們在日常使用Handler時,經常繼承它並實現handleMessage方法,實際並沒有必要,只要傳入Callback就可以。
Handler還有直接傳入Looper的構造方法:
public Handler(Looper looper) {
this(looper, null, false);
}
總結
通過上述原理分析,可以總結出在子線程中使用Handler的方式概括如下:
new Thread(){
@Override
public void run() {
Looper.prepare();
Handler handler = new Handler();
Looper.loop();
}
}.start();