為什么要使用Handler
-
因為在Android中訪問UI只能在主線程中進(jìn)行,如果在子線程中運行,則程序會拋出異常。
// ViewRootImpl.java void checkThread() { if (mThread != Thread.currentThread()) { throw new CalledFromWrongThreadException( "Only the original thread that created a view hierarchy can touch its views."); } }
-
為什么不允許在子線程中訪問UI?
因為Android的UI并不是線程安全的,如果在多線程中執(zhí)行UI的操作,那么UI的狀態(tài)是不可控的,這個時候就會出現(xiàn)各種問題。
那么最好的辦法就是只能在一個線程中執(zhí)行UI的操作。
而Android主線程中又不能執(zhí)行耗時操作,因為那樣就會導(dǎo)致程序的ANR。
所以Android提供了Handler這樣的機(jī)制,用來在子線程中執(zhí)行耗時操作之后,發(fā)送消息給主線程,主線程再執(zhí)行UI的更新操作。
Handler機(jī)制原理分析
這里我們以Android UI線程的Handler來分析。
Handler的創(chuàng)建
Android的應(yīng)用入口是ActivityThread.main()
函數(shù),UI線程的Handler就是在這個函數(shù)中創(chuàng)建的。
public static void main(String[] args) {
Looper.prepareMainLooper();
thread.attach(false);
ActivityThread thread = new ActivityThread();
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
Looper.loop();
}
當(dāng)main函數(shù)執(zhí)行之后,應(yīng)用就啟動了,從這個時候開始,Looper就會一直從消息隊列中取出消息并處理。而應(yīng)用會通過Handler來不斷的添加消息給消息隊列。通過不斷的添加消息和取出消息,整個消息機(jī)制就被運轉(zhuǎn)起來了。
Looper
從上面可以看到在Handler創(chuàng)建前后,通過Looper的prepare
和loop
方法,創(chuàng)建了Looper對象和開啟消息循環(huán)。
-
構(gòu)造函數(shù)
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }
- 創(chuàng)建了MessageQueue對象,這個對象是消息隊列,主要用來存放我們的消息,會在下面具體分析。
- 獲取當(dāng)前的線程并保存起來
-
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)); }
- 判斷ThreadLocal中在當(dāng)前線程是否已經(jīng)存在了Looper對象,如果存在,則拋出異常,所以可以得出在同一個線程中只能存在一個Looper對象。
- 在ThreadLocal存入當(dāng)前線程的Looper對象。
- ThreadLocal對象是一個線程內(nèi)部的數(shù)據(jù)存儲類,通過它可以在指定的線程存儲數(shù)據(jù),當(dāng)存儲數(shù)據(jù)之后,就只能在指定的線程中獲取到存儲的數(shù)據(jù),其他線程是不能獲取到數(shù)據(jù)的。
-
loop
public static void loop() { final Looper me = myLooper(); // 獲取消息隊列 final MessageQueue queue = me.mQueue; // 死循環(huán)來輪詢的從消息隊列中獲取消息 for (;;) { Message msg = queue.next(); // might block if (msg == null) { return; } try { msg.target.dispatchMessage(msg); } finally { if (traceTag != 0) { Trace.traceEnd(traceTag); } } msg.recycleUnchecked(); } }
- loop函數(shù)會不斷的從消息隊列中取出消息,當(dāng)queue.next()為空的時候就直接阻塞住
- msg不為空,調(diào)用
msg.target.dispatchMessage(msg)
處理消息,而msg.target
其實就是Handler對象,取出消息之后就交給Handler來處理發(fā)送的消息了。
-
quit
public void quit() { mQueue.quit(false); } public void quitSafely() { mQueue.quit(true); }
調(diào)用MessageQueue的quit方法來退出Looper。其中上面的是直接退出,下面的是安全的退出,也就是只標(biāo)記一下,當(dāng)消息隊列中的消息全部執(zhí)行完成之后安全的退出。
當(dāng)我們在子線程中創(chuàng)建Looper和Handler的時候,如果我們不調(diào)用quit方法的話,子線程就會一直處于等待狀態(tài),所以我們需要調(diào)用quit方法退出Looper。
MessageQueue
在Looper中大量的使用到了MessageQueue,而MessageQueue主要就是兩個操作插入消息和讀取消息并刪除消息,我們來一起分析下:
-
構(gòu)造函數(shù)
MessageQueue(boolean quitAllowed) { mQuitAllowed = quitAllowed; mPtr = nativeInit(); }
在構(gòu)造函數(shù)中,調(diào)用了
nativeInit
方法在Native層創(chuàng)建了NativeMessageQueue和Looper,并將Looper設(shè)置給當(dāng)前的線程。這個時候Java層和Native層都有了MessageQueue和Looper。
在Native層的Looper中,創(chuàng)建了一個管道,本質(zhì)上就是一個文件,一個管道中有讀和寫兩個文件操作符,通過讀和寫來將消息寫入和讀取。 -
enqueueMessage 插入消息
boolean enqueueMessage(Message msg, long when) { synchronized (this) { msg.markInUse(); msg.when = when; Message p = mMessages; boolean needWake; if (p == null || when == 0 || when < p.when) { // New head, wake up the event queue if blocked. msg.next = p; mMessages = msg; needWake = mBlocked; } else { needWake = mBlocked && p.target == null && msg.isAsynchronous(); Message prev; for (;;) { prev = p; p = p.next; if (p == null || when < p.when) { break; } if (needWake && p.isAsynchronous()) { needWake = false; } } msg.next = p; // invariant: p == prev.next prev.next = msg; } // We can assume mPtr != 0 because mQuitting is false. if (needWake) { nativeWake(mPtr); } } return true; }
enqueueMessage其實就是構(gòu)建好msg,并插入到單鏈表中。
-
next 讀取消息并刪除消息
Message next() { for (;;) { nativePollOnce(ptr, nextPollTimeoutMillis); } }
next方法是一個無限循環(huán)方法,當(dāng)消息隊列中沒有消息的時候,next方法會被阻塞在這里,當(dāng)有新的消息的時候,next方法會最終返回這條消息并從單鏈表中移除。
Handler
-
構(gòu)造函數(shù)
public Handler(Callback callback, boolean async) { 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; }
- 首先獲取Looper對象,如果Looper對象為空,則拋出異常,所以當(dāng)我們在子線程中創(chuàng)建Handler的時候,首先要通過
Looper.prepare()
來創(chuàng)建子線程的Looper對象。 - 通過Looper獲取到MessageQueue
- 首先獲取Looper對象,如果Looper對象為空,則拋出異常,所以當(dāng)我們在子線程中創(chuàng)建Handler的時候,首先要通過
-
發(fā)送消息
Handler的發(fā)送消息主要是post的方法和send的方法,而他們最終調(diào)用的都是:
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); } private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
Handler主要就是向消息隊列中插入了一條消息,這個時候MessageQueue就會調(diào)用插入的方法來插入消息。
-
讀取消息
當(dāng)Handler發(fā)送消息之后,MessageQueue就會通過next方法讀取出這個消息,那么Looper的loop就不會被阻塞住,取出消息之后就調(diào)用了
msg.target.dispatchMessage(msg)
方法,最終交給Handler的dispatchMessage
來執(zhí)行。/** * 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是否為空,不為空則通過
handleCallback
來處理,而callback就是通過post傳遞過來的Runnable對象。直接調(diào)用run方法執(zhí)行。private static void handleCallback(Message message) { message.callback.run(); }
-
檢查mCallback是否為空,不為空則調(diào)用
mCallback.handleMessage
方法處理消息。而mCallback
就是我們在創(chuàng)建Handler的時候傳遞過去的callback。public interface Callback { public boolean handleMessage(Message msg); } public Handler(Callback callback) { this(callback, false); }
調(diào)用
handleMessage(msg)
處理消息。
-