looper 的prepare()
方法**如下:
private
static
void
prepare(boolean quitallowed)
sthreadlocal.set(new looper(quitallowed));
}
這個方法裡面最重要的無疑就是 sthreadlocal 物件的 get 和 set 方法了,而剛才我們說的那個異常資訊,就是通過 sthreadlocal.get() 的返回值進行判斷,sthreadlocal 是 threadlocal 的物件,要明白」如何在乙個執行緒中只保留乙份 looper」,就需要看看 threadlocal 的內部結構是什麼,為了明白 threadlocal 的內部結構,我們先從它的 set 方法入手,set 方法的原始碼如下:
public
void
set(t value)
注意,thread 類裡面是包含有 threadlocal.threadlocalmap 的物件 threadlocals,localthread 的 get 方法也很簡單,**如下:getmap(thread t)
方法正是通過t.threadlocals
來獲取 threadlocalmap 物件的。
public t get()
return setinitialvalue();
}
這裡面同樣獲取了當前執行緒物件,根據執行緒物件來獲取乙個 threadlocalmap,然後獲取我們儲存的值,如果 map 不存在或者 threadlocalmap.entry 為 null,則呼叫 setinitialvalue() 方法,該方法其實就是將 t 初始化為 null,然後建立乙個 threadlocalmap 物件,並將 threadlocal 物件和 t 的值(null)傳入。
那麼回到looper.prepare()
方法,sthreadlocal.get()
的意思就是根據當前執行緒物件來獲取乙個 threadlocal 物件。如果為 null 則丟擲異常,如果不為 null,則呼叫 set 方法,先建立乙個 looper 物件,並將該 looper 物件傳入 set 方法中,這樣就在當前執行緒中儲存了乙份該 looper 物件。
looper 物件建立完成之後,我們會呼叫 looper.loop() 這個方法來啟動 looper 的迴圈,loop 方法的原始碼如下:
public
static
void
loop()
// 從 looper 物件中獲取 messagequeue
final messagequeue queue = me.mqueue;
......
// 乙個好可怕的死迴圈!!!
for (;;)
......
try finally
}......
// ** message 物件,因此通過 message msg = message.obtain() 或 handler.obtainmessage() 來獲取乙個 message 物件比使用 new mesage() 更高效。
msg.recycleunchecked();
}}
上面將原始碼進行了簡單的注釋,解釋了 loop 方法裡面的執行流程。大概就是先獲取到 messagequeue 中的 message 物件,如果 message 物件不為 null,則呼叫 dispatchmessage 方法將其交與 handler 處理,並** message 物件。
而 dispatchmessage 方法原始碼如下:
public
void
dispatchmessage(message msg) else
}handlemessage(msg);
}}
msg.callback 是乙個 runnable 物件,獲取 message 的方法除了 message.obtain(handler h) 外還有乙個 message.obtain(handler h, runnable callback) 方法。也就是說,如果我們使用 message.obtain(handler h) 方法建立了 message 物件,並呼叫 sendmessage() 方法,那麼最終會返回到 handler 的 handlemessage() 方法中處理訊息,而如果我們使用的是 message.obtain(handler h, runnable callback) 方法建立的 message 物件,並且 callback 不為 null,那麼最終會呼叫 handler 的 handlecallback 方法來處理訊息,也就是執行 callback 的 run 方法。
looper 裡面的相關流程就基本介紹完了,可能有童鞋會對 loop 方法有疑問:loop 中的死迴圈難道不會阻塞主線程麼?對於這個問題,知乎上面有乙個非常好的回答:android中為什麼主線程不會因為looper.loop()裡的死迴圈卡死?。
說完了 looper,下面該說說 messagequeue 了,畢竟在 loop 裡面,message 物件是通過 messagequeue 獲取的。messagequeue 是乙個由單鏈表實現的先入先出佇列,既然是乙個佇列,那麼插入和取出是最主要的操作了。在 messagequeue 當中,插入方法是 enqueuemessage,而取出方法使 next。
由於之前 looper.loop 中出現了 next 方法,那我們就先看一下這個方法的原始碼:
message next() while (msg != null && !msg.isasynchronous());
}if (msg != null) else else
msg.next = null;
if (debug) log.v(tag, "returning message: " + msg);
msg.markinuse();
return msg;
}} else
......
}}
messagequeue 的 next 方法其實就是不斷地從 messagequeue 維護的鍊錶中獲取 message 物件。那麼我們再來看 :
boolean enqueuemessage(message msg, long when) else
if (needwake && p.isasynchronous())
}msg.next = p; // invariant: p == prev.next
prev.next = msg;
}......
}return
true;
}
也是有乙個死迴圈,不斷地將 message 追加到 messagequeue 的鍊錶當中。
next 方法是在 looper.loop 方法中呼叫,而 enqueuemessage 方法自然就是在 handler 中呼叫了。我們在使用 handler 來傳送乙個訊息時,無論是使用 sendmessage 還是 post(runnable),最終都會呼叫到 sendmessageattime 這個方法,而在這個方法裡面就完成了將 message 新增到 messagequeue 的操作:
public
boolean
sendmessageattime(message msg, long uptimemillis)
return enqueuemessage(queue, msg, uptimemillis);
}
而 handler 中的 enqueuemessage 方法則是呼叫了 messagequeue 中的 enqueuemessage 方法,來將 message 新增到 messagequeue 中:
private
boolean
enqueuemessage(messagequeue queue, message msg, long uptimemillis)
return queue.enqueuemessage(msg, uptimemillis);
}
至此,我們從尾到頭地將 handler 與非同步訊息處理機制的原始碼梳理了一遍。那麼我們再從頭到尾地簡單總結一下整個流程:handler 將 message 物件,通過呼叫 enqueuemessage 方法新增到 messagequeue 這個單鏈表構成的佇列中,然後 looper.loop 不斷地呼叫 messagequeue 的 next 方法取出 message 物件,並將其返還給 handler 處理,或者呼叫 message.callback 的 run 方法執行任務。 Android非同步訊息處理Handler的使用
眾所周知,android的ui執行緒是不安全的,其它執行緒不可以直接操作ui。那麼就產生了乙個問題 android非同步訊息處理 如何在其它執行緒進行ui更新操作。由於ui都是由主線程繪製的,在進行耗時操作的時候會導致主線程繁忙,最後導致ui介面卡頓或無反應,所以在進行耗時操作的時候我們應該新建一條...
非同步訊息處理機制 Handler
handler面試詳解 一 什麼是handler handler通過傳送和處理 message 和runnable 物件來關聯對應執行緒的 messagequeue.1.可以讓對應的message和 runnable 在未來的某個時間點進行相應處理 2.讓自己想要處理的耗時操作放在子執行緒,讓更新 ...
Handler 的偽非同步與真非同步
什麼是handler,已經有很多人介紹過了,在這裡重點記下最近對handler的理解。handler如果不與looper一起呼叫,那麼它與activity其實是乙個執行緒,在這裡先貼下 package lxf.handlertest import android.os.bundle import a...