Handler到底是如何完成線程切換的?這個問題要從Handler最初的用法和原理講起。
首先我們列出正常情況下一個Handler使用的步驟然後講解分析如何實現,這裏不對一些基礎的概念做解釋,具體的請查閱源碼。
Handler的使用步驟:
1.調用Looper.prepare();
2.創建Handler對象;
3.調用Looper.Loop()方法。
4.線程中發送消息。
這樣我們就創建好了一個Handler可以採用sendMessage等方法發送消息。
那麼這幾步都幹了什麼?
第一步:調用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));
}
創建了一個Looper對象保存在了一個ThreadLocal對象中,至於ThreadLocal是幹什麼的,查詢有關ThreadLocal如何存儲線程中數據的資料。
上面代碼中創建了一個Loper對象,new Looper的時候我們可以看到:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
// 創建了一個MessageQueue對象,這個就是一個消息隊列用來保存我們發送的Message,其實這個MessageQUueue是一個單鏈表。
後邊我們會知道這裏創建了一個MessageQueue對象,從字面上來看就是一個消息隊列,其實他是一個鏈表,鏈表的數據結構就是便於插入和刪除。
第二步創建Handler
public Handler(Callback callback, boolean async) {
......
mLooper = Looper.myLooper();
......
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
從構造方法中我們知道了Hanlder中獲取了一個Looper對象,這個Looper中保存了一個mQueue.這個Lopper對象就是我們前面保存在ThreadLocal中的那個。
第三步調用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;
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;
}
......
final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
final long end;
try {// 處理消息
msg.target.dispatchMessage(msg);
......
}
我們清楚的看到:loop()方法中獲取了Looper對象,取出了消息隊列mQueue,然後開始循環。同時調用了MessageQueue的next()方法取出一個消息最後調用了這個消息的dispathchMessage();看完源碼我們知道這個msg.tagget.dispatchMessage()就是Handler中的消息處理的方法。
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
這個方法的代碼中我們看到,首先判斷了callback是不是null.這是因爲Handler使用有兩種方法,一種可以通過創建CallBack的方式。最後調用了Hanlder的handleMessage(msg);這個方法就是我們經常自己實現的消息處理方法。所以我們就到達了目標。
第四步:發送消息
調用sendMessage()等方法發送消息,追蹤發送消息的方法,最後調用了以下的方法:
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);
}
這裏拿到了我們前面Handler構造方法保存的mQueue,然後調用了enqueueMessage()方法。
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
最後看queue.enqueueMessage(msg, uptimeMillis);在queue中處理了消息。
boolean enqueueMessage(Message msg, long when) {
synchronized (this) {// 同步的操作,同一時間只能一個線程操作
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
......
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;
}
}
return true;
}
也就是通過消息要執行的時間,最後把消息插入鏈表中。
這樣我們就完成了消息的發送!
此刻:主線程的loop()方法一直在循環處理着消息,我們發送的消息就會到loop()方法中去,最後交給Handler處理。
那麼到底是怎麼樣就完成了線程的切換呢?
其實就是這個消息發送的過程,我們在不同的線程發送消息,線程之間的資源是共享的。也就是任何變量在任何線程都可以修改,只要做併發操作就好了。上述代碼中插入隊列就是加鎖的synchronized,Handler中我們使用的是同一個MessageQueue對象,同一時間只能一個線程對消息進行入隊操作。消息存儲到隊列中後,主線程的Looper還在一直循環loop()處理。這樣主線程就能拿到子線程存儲的Message對象,在我們沒有看見的時候完成了線程的切換。
所以總結來講就是:
1.創建了一個Looper對象保存在ThreadLocal中。這個Looper同時持有一個MessageQueue對象。
2.創建Handler獲取到Looper對象和MessageQueue對象。在調用sendMessage方法的時候在不同的線程(子線程)中把消息插入MessageQueue隊列。
3.在主線程中(UI線程),調用Looper的loop()方法無限循環查詢MessageQueue隊列是否有消息保存了。有消息就取出來調用dispatchMessage()方法處理。這個方法最終調用了我們自己重寫了消息處理方法handleMessage(msg);這樣就完成消息從子線程到主線程的無聲切換。
最後補充:我們經常使用Handler沒有創建Looper調用Looper.pepare()和Looper.loop()是因爲在ActivityThread中已經創建了主線程的Looper對象,保存在了ThreadLocal中,我們在創建Hander的時候就會從ThreadLocal中取出來這個Looper。
從主線程類ActivityThread的入口方法main()方法中我們可以清楚的看到系統幫我們如何實現的:
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
SamplingProfilerIntegration.start();
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
// Set the reporter for event logging in libcore
EventLogger.setReporter(new EventLoggingReporter());
// Make sure TrustedCertificateStore looks in the right place for CA certificates
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper();// 調用了prepareMainLooper(),創建Looper和MessageQueue
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();// 獲取Handler
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();// 開始調用loop()方法
throw new RuntimeException("Main thread loop unexpectedly exited");
}