Handler與非同步訊息的原始碼解析

2021-07-24 18:20:02 字數 4387 閱讀 4129

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,getmap(thread t)方法正是通過t.threadlocals來獲取 threadlocalmap 物件的。

localthread 的 get 方法也很簡單,**如下:

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...