Android消息機制:Handler、Looper、MessageQueue對比分析,在Andrid啟動的時候,默認會有一個主線程,也就是UI線程,在Android3.0之後的版本中,Android禁止在主線程中進行網絡請求,通常情況下網絡請求會一個極其耗時的操作,所以耗時操作會嚴重影響用戶的體驗,甚至會出現ANR,但是有一個需求永遠存在,就是在耗時操作之後,把耗時操作之後的結果更新到界面上來。一般情況下,可以這麼用:
new Thread(new Runnable(){ @override public void run(){ //這個是隨意定義的耗時操作 String response = HttpUtils.post(url,map); //這個時候想把獲取的response顯示到界面上去,但是隻有UI線程才能更新UI界面 //而且這個子線程獲取到的response與顯示界面的操作得同步,我們可以這麼用 runOnUiThread(new Runnable(){ @override public void run(){ //將網絡獲取到的結果顯示到UI界面上去 mTvDisplay.setText(response); } }); } }).start();
而runOnUiThread()方法底層則是調用瞭
Android提供的異步消息處理機制
Handler,該方法是對異步消息處理機制的一個封裝,這個異步消息處理機制
Handler處理消息的過程可以簡單地分為:
Message、
Handler、
MessageQueue、
Looper,他們的關系如下:
這張圖可以一目瞭然地將他們之間的關系描述得非常清楚,他們的處理過程是這樣的:
UI線程的消息循環是在ActivityThread.main()方法中創建的,源碼如下:
public static void main(String[] args){
...
Looper.prepareMainLooper();//創建消息循環Looper
ActivityThread thread = new ActivityThread();
thread.attach(false);
if(sMainThreadHandler == null){
sMainThreadHandler = thread.getHandler();//UI線程的Handler
}
...
Looper.loop();//執行消息循環
}
開始執行從
main()方法開始,如何將
Message投遞到
MessageQueue中,如何將
Message從
MessageQueue中取出,這其中的關鍵就是
Handler。其實每一個
Handler都會關聯一個消息隊列,消息隊列被封裝在
Looper中,而每個
Looper又會關聯一個線程(
Looper通過
ThreadLocal封裝),最終就等於每個消息隊列會關聯一個線程。默認情況下,消息隊列隻有一個,就是UI線程的消息隊列,它是在
main方法中被 創建的,
Looper.prepareMainLooper()方法來創建,
Looper.loop()來啟動消息循環,
Handler是如何關聯消息隊列以及線程的,看源碼:
public Handler(){
...
mLooper = Looper.myLooper();//獲取Looper
if(mLooper == null){
//throw Exeception;
}
mQueue = mLooper.mQueue;//獲取消息隊列
mCallback = null;
}
public static Looper myLooper(){
return sThreadLocal.get();
}
//設置UI線程的Looper
public static void prepareMainLooper(){
prepare();
setMainLooper(myLooper());
myLooper().mQueue.mQuitAllowed = false;
}
private synchroinzed static void setMainLooper(Looper looper){
mMainLooper = looper;
}
//為當前線程設置一個Looper
public static void prepare(){
if(sThreadLocal.get() != null){
//throw Exception
}
sThreadLocal.set(new Looper());
}
Handler的構造函數中看到,
Looper是通過
Looper.myLooper()來獲取的,然後看
myLooper()方法,直接通過
sThreadLocal.get()方法獲取的,那麼既然能夠獲取就肯定已經存儲在裡面瞭,什麼時候把
Looper存進去呢,看
prepareMainLooper()方法,調用瞭
prepare()方法,在
prepare()方法的末端,直接
new Thread()存儲在瞭
sThreadLocal中,到這裡,隊列就跟線程關聯上瞭,代表著不同的線程就不能訪問別的消息隊列。
回到
Handler中來,消息隊列通過
Looper與線程關聯上,而
Handler又與
Looper關聯,因此
Handler最終就和線程、線程的消息隊列關聯上瞭。所以,更新UI的
Handler必須i要在主線程中創建,因為
Handler要與主線程的消息隊列關聯上,這樣
handleMessage才會執行在UI線程。
Looper怎麼創建的知道瞭,
Looper怎麼循環起來的呢,是通過
looper()方法,源碼如下:
public static void loop(){
Looper me = myLooper();
if(me == null){
//throw Exception
}
MessageQueue queue = me.mQueue;//獲取消息隊列
...
while(true){
Message msg = queue.next();//不斷獲取消息
if(msg != null){
if(msg.target == null){
return;
}
...
msg.target.dispatchMessage(msg);//處理消息
msg.recycle();
}
}
}
一個死循環,代表著不斷從
MessageQueue中取消息,對於
Looper的步驟大概就是以下兩步:
1.通過
Looper.prepare()來創建
Looper對象,消息隊列在
Looper對象中,存儲在
sThreadLocal
2.通過Looper.loop()來啟動消息循環
從上述的
loop()源碼中可以看出,最最最有用的一行代碼就是
msg.target.dispatchMessage(msg),那麼
target是什麼,
dispatchMessage(msg)又拿著這個
msg去幹瞭什麼事,接下來繼續看
Message的成員變量:
public final class Message implements Parcelable{
Handler target;
Runnable callback;
Message next;
...
}
顯然
target是
Handler類的對象,那麼
dispatchMessage(msg)就是
Handler拿著去幹瞭什麼,看
Handler其中的幾個方法:
//一般new的Handler都會重寫這個方法,用來實現自己的自定義業務
public void handleMessage(Message msg){
}
private final void handleCallback(Message message){
message.callback.run();
}
public void dispatchMessage(message msg){
if(msg.callback != null){
handleCallback(msg);
} else {
if(mCallback != null){
if(mCallback.handleMessage(msg)){
return;
}
}
handleMessage(msg);
}
}
上述這段代碼的意思就是,
sHandler.post(new Runnable()),那麼就直接調用這個
Runnable的
run(),如果是直接
sendMessage(msg)的話,就直接調用重寫的
handleMessage(),這裡可以看一下
post()和
sendMessage()的兩種實現,先看
post():
public final boolean post(Runnable r){
return sendMessageDelayed(getPostMessage(r),0);
}
private final Message getPostMessage(Runnable r){
Message m = Message.obtain();
m.callback = r;
return m;
}
public final boolean sendMessageDelayed(Message msg,long delayMillis){
if(delayMillis < 0){
delayMillis = 0;
}
return sendMessageAtTime(msg,SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(Message msg,long uptimeMillis){
boolean sent = false;
MessageQueue queue = mQueue;
if(queue != null){
msg.target = this;//將target設置為當前的Handler對象
sent = queue.enqueueMessage(msg,uptimeMillis);//將消息插入到消息隊列
} else {
...
}
return sent;
}
從上面可以看出,
Runnable被設置到瞭
Message的
Runnable中去瞭,然後就直接把
Message扔到瞭
MessageQueue中,再看
sendMessage();
public final boolean sendMessage(Message msg){
return sendMessageDelayed(msg,0);
}
由此可見,無論是
post()方法還是
sendMessage()方法,他們內部都是經過一步一步的調用,調用到瞭
sendMessageDelayed()這個方法,然後通過打包成
Message再把他們扔進瞭
MessageQueue中,然後
Looper從
MessageQueue不斷讀取消息,然後扔給瞭
Message.target也就是
Handler的
dispatchMessage()這個方法不斷去處理消息,無論是
post的
Runnable還是
handleMessage的處理方式,經過
dispatchMessage中的判定去執行。
所以!
發送消息是
Handler的
post()
方法和
sendMessage()
方法,
處理消息的還是Handler的
handleMessage()
和
handleCallback()
(通過Runnable中的run()方法去執行),
由此可見,有瞭Handler,消息循環才能運轉起來。
runOnUiThread()的實現過程大概是這樣:
事件響應→開啟線程→runOnUiThread→判斷是否當前線程為主UI線程(是就立刻執行,不是就通過handler.post()發送到動作序列中,等到是主UI線程再立刻執行)
Handler的實現過程大概是這樣:
事件響應→開啟線程→new Message()→handler.sendMessage(msg);→new handler(msg.getdata→getString→更改UI)
由此可見,通過
Handler可以更加精細地控制自己的業務邏輯更好的在一些復雜的情況下進行合理的操作。
Android的異步消息機制大概就這樣,過程使用,源碼分析,如有不對指出望請指出批評,謝謝。