Android中,當我們需要在子線程中進行網絡請求等耗時操作后,如果需要更新UI時,通常會考慮使用Handler來處理,一種常用的寫法如下:
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what == 1){
// update ui
}
}
};
new Thread(new Runnable() {
@Override
public void run() {
Message message = Message.obtain();
message.what = 1;
mHandler.sendMessage(message);
}
});
除了上邊我們用到的Handler類外,Android消息機制要正常工作還需要Looper和MessageQueue兩個類的配合,下來我們分別介紹三個類,以及它們之間的工作原理:
1、MessageQueue
顧名思義就是消息隊列,但其實MessageQueue的底層是通過單鏈表來實現(xiàn)的,MessageQueue是用來中轉Handler對象發(fā)送出的消息,主要包括enqueueMessage和next兩個操作,分別用來插入一條消息和取出一條消息,因為鏈表在插入和刪除操作上有較高的效率,這可能就是使用該數(shù)據(jù)結構的原因吧。MessageQueue只負責保存消息,并不會處理消息,MessageQueue對象如何得到呢?繼續(xù)往下看。
2、Looper
如果我們是在UI線程中使用Handler來發(fā)送異步消息,系統(tǒng)已經在當前UI線程通過Looper.prepareMainLooper()幫助我們創(chuàng)建好Looper對象,并調用Looper.loop()開啟無限消息循環(huán),不斷從MessageQueue的實例中讀取消息。如果在子線程中我們不手動創(chuàng)建Looper對象,則會拋出java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()的異常,所以在子線程中創(chuàng)建并使用Handler對象,就必須手動調用Looper.prepare()創(chuàng)建Looper對象,并通過Looper.loop()開啟消息循環(huán)。
在子線程中可通過如下方式使用Android消息機制:
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();//創(chuàng)建Looper對象
Handler handler = new Handler();
handler.post(new Runnable() {
@Override
public void run() {
//do something
}
});
Looper.loop();//開啟消息循環(huán)
}
});
這樣子線程同樣具有了異步消息循環(huán)處理的能力。
我們先通過源碼分析一下Looper.prepare()方法
public static void prepare() {
prepare(true);
}
很簡單,只有一行代碼,繼續(xù)進入到prepare方法中
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));
}
重點看一下new Looper()方法
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
可以看到在創(chuàng)建Looper對象時也創(chuàng)建了MessageQueue對象,此時Looper對象持有了MessageQueue對象的引用。并與當前線程綁定,保證一個線程只會有一個Looper對象,同時一個Looper對象也只有一個MessageQueue對象。
3、Handler
首先看一下Handler的構造方法
public Handler() {
this(null, false);
}
依然只有一行,繼續(xù)跟進
public Handler(Callback callback, boolean async) {
........
省略
........
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;
}
通過mLooper = Looper.myLooper(),我們Handler對象拿到了對應Looper對象的引用,產生了關聯(lián)。
可以看到如果Looper等于null的話會拋出異常,這也驗證了上邊第二點中的說法,創(chuàng)建Handler對象時必須有Looper對象存在。
再看 mQueue = mLooper.mQueue這一行,其實就是將Handler的實例與我們Looper實例中創(chuàng)建的MessageQueue對象關聯(lián)起來。此時MessageQueue對象已經和Looper對象以及Handler對象關聯(lián)了起來,并且他們在同一線程中。
4、工作原理
結合文章開頭的例子來分析 Handler 具體的工作原理。
通過Handler對象發(fā)送消息時可用的方法非常多,例如常用的sendMessage系列方法,post系列方法等,通過源碼可以知道,這兩系列方法都最終都是調用下邊的方法來實現(xiàn)的:
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
繼續(xù)看下sendMessageAtTime方法
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);
}
其核心的的就是最后一行,跟進去
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,將Handler對象賦值給的Message對象的target屬性,此時Message對象持有了Handler對象的引用。
最后一行通過調用MessageQueue 對象的enqueueMessage方法來保存發(fā)送的消息。
上邊Looper類中還有一個Looper.loop()方法沒分析,調用該方法后,則會一直檢測MessageQueue對象中是否有數(shù)據(jù),有的話則取出,否則阻塞,此時我們來看一下源碼:
public static void loop() {
........
省略
........
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);
........
省略
........
}
}
只看核心的代碼,首先是一個無限的for循環(huán),循環(huán)中調用MessageQueue 的next方法不斷的取出Message對象,沒有消息則進入阻塞狀態(tài)。否則執(zhí)行msg.target.dispatchMessage(msg),這里的msg.target就是上邊enqueueMessage方法中的msg.target,也就是Handler對象的引用,所以此時消息就可以調用Handler的dispatchMessage()方法進行消息處理。此時Handler對象發(fā)出的消息在MessageQueue對象中通過 Looper 完成了中轉,由于Looper是在主線程創(chuàng)建并開啟消息循環(huán)的,所以dispatchMessage()在主線程被調用:
接下來看一下dispatchMessage方法:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
首先看第一行if判斷,什么時候msg.callback != null成立呢,其實就是當我們通過如下形式發(fā)送消息時
handler.post(new Runnable() {
@Override
public void run() {
// update ui
}
});
繼續(xù)查看post方法
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
進入getPostMessage方法中
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
記住m.callback = r這一行,此時再看dispatchMessage中第一個if條件的執(zhí)行代碼:
private static void handleCallback(Message message) {
message.callback.run();
}
方法中執(zhí)行的正是callback 中的run方法
if (mCallback != null)什么時候成立呢?當我們通過如下方式使用Handler時成立
private Handler mHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
return false;
}
});
從而下邊的代碼得到執(zhí)行
if (mCallback.handleMessage(msg)) {
return;
}
即就是我們上邊new Handler.Callback()中的handleMessage方法
最后如果我們采用文章最開始的代碼執(zhí)行handler操作則會執(zhí)行dispatchMessage中最后一行handleMessage(msg)方法:
public void handleMessage(Message msg) {}
可以看到是一個空的方法,因為需要我們自己去實現(xiàn)哦!同時 Handler 也是在主線程創(chuàng)建的,所以 handleMessage()
方法最終在主線程執(zhí)行,這樣就完成了從子線程到主線程的切換。
最后說一下,創(chuàng)建Message對象,可以通過new方法 ,也可以使用Message.obtain()方法,但建議使用obtain方法,因為Message內部維護了一個Message池用于Message的復用,避免使用new 重新分配內存。
到這里你有沒有發(fā)現(xiàn)Looper
、MessageQueue
、Handler
之間的關系呢?
- 在Looper中創(chuàng)建了MessageQuene,Looper循環(huán)從MessageQuene取出消息,由于消息持有Handler的引用,最終會調用Handler的handleMessage()方法或其它方法,Looper會被保存在ThreadLocal中,一個線程對應唯一Looper。
- Handler發(fā)送消息時,會從ThreadLocal中取出對應線程的Looper,再從中得到對應的MessageQuene,發(fā)送的消息會持有Handler的引用,然后將消息保存到MessageQuene。
- 關于ThreadLocal在下一篇有講到
到此Android消息機制的基本原理已經分析完了,簡單的概括一下:通過Handler對象的相關方法將Message對象發(fā)送出去進而插入到MessageQueue對象中,然后Looper對象調用loop方法,進一步會執(zhí)行MessageQueue對象的next方法取出消息,交給Handler對象的dispatchMessage方法來處理。這就是Android消息機制一個大致的流程。