為什么要使用Handler
-
因為在Android中訪問UI只能在主線程中進行,如果在子線程中運行,則程序會拋出異常。
// 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并不是線程安全的,如果在多線程中執行UI的操作,那么UI的狀態是不可控的,這個時候就會出現各種問題。
那么最好的辦法就是只能在一個線程中執行UI的操作。
而Android主線程中又不能執行耗時操作,因為那樣就會導致程序的ANR。
所以Android提供了Handler這樣的機制,用來在子線程中執行耗時操作之后,發送消息給主線程,主線程再執行UI的更新操作。
Handler機制原理分析
這里我們以Android UI線程的Handler來分析。
Handler的創建
Android的應用入口是ActivityThread.main()
函數,UI線程的Handler就是在這個函數中創建的。
public static void main(String[] args) {
Looper.prepareMainLooper();
thread.attach(false);
ActivityThread thread = new ActivityThread();
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
Looper.loop();
}
當main函數執行之后,應用就啟動了,從這個時候開始,Looper就會一直從消息隊列中取出消息并處理。而應用會通過Handler來不斷的添加消息給消息隊列。通過不斷的添加消息和取出消息,整個消息機制就被運轉起來了。
Looper
從上面可以看到在Handler創建前后,通過Looper的prepare
和loop
方法,創建了Looper對象和開啟消息循環。
-
構造函數
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }
- 創建了MessageQueue對象,這個對象是消息隊列,主要用來存放我們的消息,會在下面具體分析。
- 獲取當前的線程并保存起來
-
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中在當前線程是否已經存在了Looper對象,如果存在,則拋出異常,所以可以得出在同一個線程中只能存在一個Looper對象。
- 在ThreadLocal存入當前線程的Looper對象。
- ThreadLocal對象是一個線程內部的數據存儲類,通過它可以在指定的線程存儲數據,當存儲數據之后,就只能在指定的線程中獲取到存儲的數據,其他線程是不能獲取到數據的。
-
loop
public static void loop() { final Looper me = myLooper(); // 獲取消息隊列 final MessageQueue queue = me.mQueue; // 死循環來輪詢的從消息隊列中獲取消息 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函數會不斷的從消息隊列中取出消息,當queue.next()為空的時候就直接阻塞住
- msg不為空,調用
msg.target.dispatchMessage(msg)
處理消息,而msg.target
其實就是Handler對象,取出消息之后就交給Handler來處理發送的消息了。
-
quit
public void quit() { mQueue.quit(false); } public void quitSafely() { mQueue.quit(true); }
調用MessageQueue的quit方法來退出Looper。其中上面的是直接退出,下面的是安全的退出,也就是只標記一下,當消息隊列中的消息全部執行完成之后安全的退出。
當我們在子線程中創建Looper和Handler的時候,如果我們不調用quit方法的話,子線程就會一直處于等待狀態,所以我們需要調用quit方法退出Looper。
MessageQueue
在Looper中大量的使用到了MessageQueue,而MessageQueue主要就是兩個操作插入消息和讀取消息并刪除消息,我們來一起分析下:
-
構造函數
MessageQueue(boolean quitAllowed) { mQuitAllowed = quitAllowed; mPtr = nativeInit(); }
在構造函數中,調用了
nativeInit
方法在Native層創建了NativeMessageQueue和Looper,并將Looper設置給當前的線程。這個時候Java層和Native層都有了MessageQueue和Looper。
在Native層的Looper中,創建了一個管道,本質上就是一個文件,一個管道中有讀和寫兩個文件操作符,通過讀和寫來將消息寫入和讀取。 -
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其實就是構建好msg,并插入到單鏈表中。
-
next 讀取消息并刪除消息
Message next() { for (;;) { nativePollOnce(ptr, nextPollTimeoutMillis); } }
next方法是一個無限循環方法,當消息隊列中沒有消息的時候,next方法會被阻塞在這里,當有新的消息的時候,next方法會最終返回這條消息并從單鏈表中移除。
Handler
-
構造函數
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對象為空,則拋出異常,所以當我們在子線程中創建Handler的時候,首先要通過
Looper.prepare()
來創建子線程的Looper對象。 - 通過Looper獲取到MessageQueue
- 首先獲取Looper對象,如果Looper對象為空,則拋出異常,所以當我們在子線程中創建Handler的時候,首先要通過
-
發送消息
Handler的發送消息主要是post的方法和send的方法,而他們最終調用的都是:
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就會調用插入的方法來插入消息。
-
讀取消息
當Handler發送消息之后,MessageQueue就會通過next方法讀取出這個消息,那么Looper的loop就不會被阻塞住,取出消息之后就調用了
msg.target.dispatchMessage(msg)
方法,最終交給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是否為空,不為空則通過
handleCallback
來處理,而callback就是通過post傳遞過來的Runnable對象。直接調用run方法執行。private static void handleCallback(Message message) { message.callback.run(); }
-
檢查mCallback是否為空,不為空則調用
mCallback.handleMessage
方法處理消息。而mCallback
就是我們在創建Handler的時候傳遞過去的callback。public interface Callback { public boolean handleMessage(Message msg); } public Handler(Callback callback) { this(callback, false); }
調用
handleMessage(msg)
處理消息。
-