本文和大家聊聊Handler工作機(jī)制這檔子事因篇。
什么是Handler沪停?
官方定義:
A Handler allows you to send and process Message and Runnable objects associated with a thread's MessageQueue. Each Handler instance is associated with a single thread and that thread's message queue. When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it -- from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.
通俗的講,咱們可以把Handler理解為Android中線程與線程間通信的工具约计。
Handler結(jié)構(gòu)圖
Handler如何工作诀拭?
咱們先來通過一個常見的場景,分析Handler是如何工作的煤蚌。
第一步耕挨,咱們創(chuàng)建一個Handler實(shí)例,代碼如下:
public Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
if(msg.what == GUI_UPDATE_TEXT) {
String strValue = (String) msg.obj;
mTextView.setText(strValue);
}
}
};
第二步尉桩,咱們定義一個工作者線程筒占,用來處理一些阻塞操作,代碼如下:
class WorkerThread extends Thread{
private Handler mHandler;
public WorkerThread(Handler handler){
mHandler = handler;
}
@Override
public void run(){
//do something
Message msg = mHandler.obtainMessage(GUI_UPDATE_TEXT, strValue);
mHandler.sendMessage(msg);
}
}
第三步蜘犁,執(zhí)行線程赋铝,開始干活,代碼如下:
WorkerThread workerThread = new WorkerThread(mHandler);
workerThread.start();
第一步中咱們創(chuàng)建了Handler沽瘦,看似很簡單明了的一個操作革骨,其實(shí)Handler背后做了很多工作。
Handler被創(chuàng)建時析恋,需要從當(dāng)前線程中獲取looper良哲,并獲取該looper中的消息隊列(MessageQueue)。代碼段如下:
// Handler源碼
public Handler(Callback callback, boolean async) {
...........
mLooper = Looper.myLooper(); //獲取當(dāng)前線程的looper
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue; //獲取looper中的消息隊列
.............
.............
}
第二步中線程執(zhí)行完阻塞的事情后助隧,會調(diào)動Handler的sendMessage筑凫,那sendMessage又做了什么呢滑沧?
sendMessage最終會調(diào)用sendMessageAtTime(是所有send方法的終點(diǎn)),sendMessageAtTime將消息(message)加入消息隊列(MessageQueue)中巍实,開始休息傳遞滓技。代碼段如下:
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); //將消息加入消息隊列
}
到這里,咱們要傳遞的消息已經(jīng)在消息隊列中了棚潦,那是如何通知Handler的handleMessage的呢令漂?
這就要談到looper了,looper中有一個loop()方法丸边,它會一直循環(huán)(不是輪詢)檢查消息隊列中是否有可用的消息叠必,若有可用的消息,則從消息中獲取其所屬的Handler妹窖,并調(diào)用Handler的dispatchMessage纬朝,由dispatchMessage將消息送達(dá)hanndleMessage。
Looper代碼段如下:
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;
.................
for (;;) {
Message msg = queue.next(); // 獲取可用的消息骄呼,此方法是阻塞的
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
...................
try {
msg.target.dispatchMessage(msg);//獲取message所屬的Handler共苛,調(diào)用其dispatchMessage
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
..................
...................
msg.recycleUnchecked();
}
}
Handler代碼段如下:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
至此,主線程就可以執(zhí)行UI更新蜓萄,結(jié)束消息傳遞工作隅茎。
分析到這里,仍然有一些問題困惑著我們绕德,例如:
- Looper是如何與當(dāng)前線程關(guān)聯(lián)?
- Handler的延時發(fā)送是如何實(shí)現(xiàn)摊阀?
下面咱們來具體談?wù)?/li>
Looper是如何與當(dāng)前線程關(guān)聯(lián)耻蛇?
眾所周知,Looper的初始化是通過調(diào)用prepare()方法胞此,機(jī)敏的讀者馬上就會意識到臣咖,Looper與線程的關(guān)聯(lián)一定與這個方法有關(guān)系。
沒錯漱牵,但是在講解prepare方法之前夺蛇,咱們先來看看ThreadLocal,因為它才是實(shí)現(xiàn)Looper與線程關(guān)聯(lián)的核心元素酣胀。
ThreadLocal是線程的本地化變量刁赦,即只有擁有它的線程才可以訪問,那它是如何做到這一點(diǎn)的呢闻镶?
ThreadLocal獲取到當(dāng)前線程對象甚脉,從該線程對象中獲得ThreadLocalMap(即以ThreadLocal為key的map),并將自身放入到這個map铆农,這樣就完成了線程的本地化存儲牺氨,即實(shí)現(xiàn)了只有擁有它的線程才可以訪問。
ThreadLocal代碼段如下:
public void set(T value) {
Thread t = Thread.currentThread();//獲取當(dāng)前線程對象
ThreadLocalMap map = getMap(t);//從線程對象中獲取ThreadLocalMap
if (map != null)
map.set(this, value);//若map中已經(jīng)存在ThreadLocal,則放入自身和值(這里咱們假設(shè)值是Looper)
else
createMap(t, value);
}
咱們回到prepare()方法猴凹,該方法中將Looper存入threadlocal夷狰,完成Looper與當(dāng)前線程的關(guān)聯(lián)。Looper代碼段如下:
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));
}
Handler的延時發(fā)送是如何實(shí)現(xiàn)郊霎?
延時消息發(fā)送的關(guān)鍵在MesaageQueue的enqueueMessage方法沼头,需要延時發(fā)送的消息,在加入消息隊列時歹篓,會根據(jù)當(dāng)前消息的when值對整個消息隊列進(jìn)行升序排序瘫证,當(dāng)when值超時,會被Looper獲取到庄撮,從而完成延時消息的發(fā)送背捌。
when值默認(rèn)是系統(tǒng)啟動到現(xiàn)在運(yùn)行時間,不包含設(shè)備休眠的時間
下面咱們結(jié)合代碼來看看:
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
......................
......................
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // 當(dāng)前消息的when < p.when洞斯,就將p放到當(dāng)前消息的后面
prev.next = msg;
}
.....................
.....................
}
return true;
}
我是青嵐之峰毡庆,如果讀完后有所收獲,歡迎點(diǎn)贊加關(guān)注烙如!