Handler
Handler 主要用于在不同的線程中互相通信,使用場景最多的應該就是在子線程中更新 UI。
與 Handler 相關的類:
Handler:處理與發送消息(Message)
Message:消息的包裝類
Looper:整個 Handler 通信的核心,接受 Handler 發送的 Message,循環周遊 MessageQueue 中的 Message,并發送至 Handler 處理
MessageQueue:儲存 Message
LocalThread:存儲 Looper 與 Looper所線上程的資訊
關系流程圖:
上圖簡單概括為:Handler 将 Message post 或者 send 到 MessageQueue 中,Looper 不斷循環從 MessageQueue 中取出 Message 交由 Handler 來處理。
Handler 消息處理
Handler 處理消息的方法為 dispatchMessage:
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
①判斷 Message.callback 是否為空,不為空則交給 callback 處理;
②判斷 Handler.callback 是否為空,不為空則調用 mCallback.handleMessage(msg) 處理;
③如果前面都為空,則調用 Handler.handleMessage(msg) 方法處理。
是以我們可以通過重寫 dispatchMessage方法來改變這種預設的消息分發順序。
一般來說,我們都是按照如下的方式使用 Message
Message message = mHandler.obtainMessage();
message.what = 0;
message.obj = "123";
mHandler.sendMessage(message);
我們很少在 Message 中使用 callback,那麼 這個 callback究竟又是什麼?
Handler 消息發送
post 方式
剛剛說到,Handler 發送 Message 有兩類方法:post 和 send 。其中最常用的就是 send 方法了,下面來看一個使用 post 的例子:
mHandler.post(new Runnable() {
@Override
public void run() {
//do something
}
});
打開 post(Runnable) 方法:
public final boolean post(Runnable r){
return sendMessageDelayed(getPostMessage(r), 0);
}
其中把 Runnable 參數通過 getPostMessage() 方法包裝成了 Message 消息,然後繼續使用 send 方式發送到 MessageQueue 中去。
那麼繼續打開 getPostMessage 方法:
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
到這裡就很明白了,先擷取一個 Message 對象,然後把 Runnable 指派給 m.callback,繼而使用 send 方式發送。也就是說,post 方法本質上也是 send 方法,隻不過對 Runnable 做了一層包裝。
PS:我們平時自定義 View 時,如果想要在主線程做某些操作一般會使用 post(Runnable r) 方法,其實這個方法也是調用了 Handler 的 post 方法,将該 Runnable send 到主線程的 MessageQueue 中。
send 方式
通過各種調用之後,最終會調用 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);
}
先擷取到 MessageQueue ,然後判斷是否為空,不為空則調用 enqueueMessage 方法
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
這裡其實最終就調用了 MessageQueue 的 enqueueMessage 方法了,這個方法就是使 Message 對象入列。
MessageQueue
MessageQueue 就是一個隊列,具有一個隊列的一般特征:
①元素入隊:
boolean enqueueMessage(Message msg, long when)
②元素出隊:
Message next()
③删除元素:
void removeMessages(Handler h, int what, Object object)
void removeMessages(Handler h, Runnable r, Object object)
Looper
在整個 Handler 通信機制中,Looper 才是重頭戲,Looper 就好比計算機的 CPU,控制整個通信系統的正常運轉。
通過上面的介紹我們知道,Handler 負責發送與處理消息,MessageQueue 負責存儲消息,Message 是消息的載體,那麼他們之間到底是怎樣的結合起來的?當然靠的是 Looper,Looper 通過循環周遊 MessageQueue ,當有消息時就發送到 Handler 中處理。
道理很簡單,但實際可能要稍微複雜點,首先這裡需要明确幾個概念:
① 每個 Thread 對應一個 Looper
② 每個 Looper 對應一個 MessageQueue
③ 每個 MessageQueue 對應多個 Message
④ 每個 Message 最多隻能指定一個 Handler 處理
當我們在普通線程中使用 Handler 時,一般方式如下:
class LooperThread extends Thread{
public Handler handler;
@Override
public void run() {
super.run();
Looper.prepare();//準備工作
handler = new Handler(){
@Override
public void dispatchMessage(Message msg) {
super.dispatchMessage(msg);
//處理消息
}
};
Looper.loop();//進入主循環
}
}
到了這裡我們大概會有一個疑問,無論是 Handler 的建立還是 Handler.sendMessage() 都沒有和 Looper 産生聯系,那麼當我們使用 Handler 發送消息時,是如何確定這個消息與 Looper 關聯起來的呢?
看上面的代碼,首先通過 Looper 的靜态方法 Looper.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));
}
先不管 sThreadLocal 是個什麼東西,首先判斷 sThreadLocal.get() 是否為空,如果為空則抛出一個運作時異常:Only one Looper may be created per thread ,這個異常很常見,當你在一個線程中多次調用 Looper.prepare(); 時就會抛出這個異常,這裡保證每個線程隻有一個 Looper 對象。如果 sThreadLocal.get() 傳回空,則調用 sThreadLocal.set(new Looper(quitAllowed)) 方法,将 Looper 對象放入 sThreadLocal 中,挺簡單的幾行代碼,邏輯也很清晰,那在看看 sThreadLocal 到底是個什麼東西。
其在 Looper 中的定義為:
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
其實到這裡我們大概能判斷出來 ThreadLocal 是一個用來存儲 Looper 的東西。
我們隻需要關心上面用到的 sThreadLocal.get 和 sThreadLocal.set 方法,先看 get:
/**
* Returns the value in the current thread's copy of this
* thread-local variable. If the variable has no value for the
* current thread, it is first initialized to the value returned
* by an invocation of the {@link #initialValue} method.
*
* @return the current thread's value of this thread-local
*/
public T get() {
Thread t = Thread.currentThread();//先擷取目前線程的對象
ThreadLocalMap map = getMap(t);
if (map != null) {
//把目前線程的對象當做 key,擷取 Looper,判斷目前線程的 Looper 是否已經存在
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value;
}
return setInitialValue();
}
這個方法主要就是擷取目前線程的 Looper 對象,再來看 set 方法:
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
大意就是把目前線程的資訊 Looper 儲存到 ThreadLocal 中。
到了這我們大概就能明白了,Looper.prepare 就是在目前線程中建立一個 Looper 對象,并且保證隻有一個 Looper 存在。
可以看到,sThreadLocal 與 Looper.prepare 都是靜态方法,Looper 對象也是儲存在靜态變量中,那麼也就是說,當我們的 App 啟動時,sThreadLocal 變量就已經存在并且儲存了主線程的 Looper,當後面我們在子線程中使用 Handler 時,就會繼續把該線程的 Looper對象儲存到 sThreadLocal 中。
羅裡吧嗦扯了半天,那麼回過頭來看看剛剛的那個問題,Handler 如何與 Looper 關聯起來?
我們來看看 Handler 的預設構造方法,最終調用的構造器代碼如下:
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
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;
}
主要在于 11 行和 16 行,調用 Looper.myLooper() 方法将 Looper 對象指派給目前 Handler 中的 mLooper,在調用 mLooper.mQueue 将 Looper 中的 MessageQueue 指派給 mQueue。
到了這裡整個 Handler 通信的機制就已經很明确了,再來個大概的總結:
① 使用 Looper.prepare(); 做初始化操作,該方法會在目前線程中建立一個唯一的 Looper 對象;
② 建立 Handler 對象,Handler 在建立時就會與目前線程中的 Looper 對象與 MessageQueue 對象綁定;
③ Looper.loop() 啟動循環。
ActivityThread 中的 Handler
ActivityThread 與普通線程使用 Looper.prepare(); 不同,這裡使用 Looper.prepareMainLooper(); 來初始化:
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
這個函數除了會調用 prepare 之外,主要的功能就是将目前線程(也就是主線程)的 Looper 指派給 sMainLooper 變量,這樣就可以使用 Looper.getMainLooper() 擷取主線程的 Looper。
另外 Handler 的建立跟普通線程也有一定的差別:
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
在 ActivityThread 的 mian 函數中,會先執行個體化一個 ActivityThread 對象,然後通過這個對象的 getHandler 方法擷取 Handler。
這個 Handler 用于處理主線程中的各種消息,其中已經定義了很多處理消息的操作,例如啟動 Activity 、記憶體管理等操作,隻要發送通過這個 Handler 發送相關的 Message 即可,而 ActivityThread 之是以要講這個 Handler 指派給其中的靜态變量 sMainThreadHandler 也是因為要通過這個 Handler來發送相關的消息。
初始化結束後就開始進入消息循環( Looper.loop )的階段,這個方法就是用于不停地接受 Handler 發送過來的各種消息,并交給 Handler 來處理:
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
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;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
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
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
final long traceTag = me.mTraceTag;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
try {
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
msg.recycleUnchecked();
}
}
這裡就是開始一個死循環,看其中的 36 行,接受到消息後交給 Handler 的 dispatchMessage 方法來處理。
點此檢視原文
如果覺得還不錯的話,歡迎關注我的個人公衆号,我會不定期發一些幹貨文章~
參考文獻
[1]林學森. 深入了解Android核心設計思想[M]. 北京, 2014.