一、核心類關系與線程綁定(ThreadLocal 的核心作用)
1.?Looper 與 ThreadLocal 的綁定
每個線程的 Looper 實例通過?ThreadLocal<Looper> sThreadLocal
?存儲,確保線程隔離:
public final class Looper {// 線程本地存儲,每個線程獨有一個 Looperstatic final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();// 主線程 Looper(ActivityThread 中創建)static Looper sMainLooper; final MessageQueue mQueue; // 關聯的消息隊列final Thread mThread; // 綁定的線程private Looper(boolean quitAllowed) {mQueue = new MessageQueue(quitAllowed);mThread = Thread.currentThread(); // 記錄當前線程}// 線程首次調用,創建 Looper 并存儲到 ThreadLocalpublic static void prepare() {prepare(false); // quitAllowed 默認 false(主線程不允許退出)}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}// 獲取當前線程的 Looperpublic static @Nullable Looper myLooper() {return sThreadLocal.get();}
}
- 主線程:Android 框架在?
ActivityThread.main()
?中自動調用?Looper.prepareMainLooper()
?和?Looper.loop()
,無需手動處理。 - 子線程:必須手動調用?
Looper.prepare()
(創建 Looper 和 MessageQueue)和?Looper.loop()
(啟動消息循環),否則 Handler 無可用 Looper 會報錯。
2.?Handler 的構造與 Looper 關聯
Handler 實例必須與一個 Looper 綁定,默認使用當前線程的 Looper(通過?Looper.myLooper()
?獲取):
public class Handler {final Looper mLooper; // 關聯的 Looperfinal MessageQueue mQueue; // Looper 的消息隊列final Callback mCallback; // 消息回調public Handler() {this(null, false);}public Handler(@Nullable 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(); // 獲取當前線程的 Looper(必須已 prepare)if (mLooper == null) {throw new RuntimeException("Can't create handler inside thread " + Thread.currentThread()+ " that has not called Looper.prepare()");}mQueue = mLooper.mQueue; // 關聯消息隊列mCallback = callback;mAsynchronous = async;}
}
- 若子線程未調用?
Looper.prepare()
,創建 Handler 時會拋出?RuntimeException
,這就是子線程必須先準備 Looper 的原因。
二、消息發送:從 Handler 到 MessageQueue 的入隊
1.?Message 的創建與重用(消息池機制)
Message 優先從消息池獲取,避免頻繁 GC:
public final class Message implements Parcelable {// 消息池頭節點(靜態,所有線程共享)private static Message sPool;// 消息池大小(最大 50 個)private static int sPoolSize = 0;// 下一個可用消息(形成單鏈表)@UnsupportedAppUsageMessage next;// 從消息池獲取消息public static Message obtain() {synchronized (sPoolSync) { // 線程安全if (sPool != null) {Message m = sPool;sPool = m.next; // 取出頭節點m.next = null; // 斷開引用m.flags = 0; // 重置標志位sPoolSize--;return m;}}return new Message(); // 池空時新建}// 回收消息到池中(處理完后調用)void recycleUnchecked() {if (isInUse()) { // 確保未被使用if (gCheckRecycle) {throw new IllegalStateException("This message cannot be recycled because it "+ "is still in use.");}return;}recycle();}private void recycle() {if (mRecycled) { // 已回收過則不再處理return;}mRecycled = true;if (sPoolSize < MAX_POOL_SIZE) { // 最大 50 個next = sPool;sPool = this;sPoolSize++;}}
}
- 優勢:減少對象創建開銷,提升性能,尤其適合高頻發送消息的場景。
2.?MessageQueue 的入隊邏輯(enqueueMessage)
消息按?msg.when
(執行時間)排序插入,形成一個?非嚴格 FIFO 的有序單鏈表:
boolean enqueueMessage(Message msg, long when) {msg.target = this; // target 指向發送消息的 Handlermsg.workSourceUid = ThreadLocalWorkSource.getUid();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) { // 新消息時間更早,插入頭部msg.next = p;mMessages = msg;needWake = mBlocked; // 當前隊列是否阻塞,決定是否喚醒} else { // 找到合適位置插入(按 when 升序)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;prev.next = msg;}// 若隊列阻塞且需要喚醒(如 Looper.loop() 在等待),通過 native 方法喚醒if (needWake) {nativeWake(mPtr);}}return true;
}
- 關鍵點:
synchronized (this)
?確保多線程安全,不同線程的 Handler 發送消息到同一 MessageQueue 時不會沖突。- 消息按?
when
?排序,而非嚴格的 FIFO,支持延遲消息(如?postDelayed
)。 - 異步消息(
msg.setAsynchronous(true)
)可打斷同步消息的等待,優先處理。
三、消息循環:Looper.loop () 的無限循環
1.?loop () 方法核心邏輯
public static void loop() {final Looper me = myLooper(); // 獲取當前線程的 Looperif (me == null) {throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");}final MessageQueue queue = me.mQueue; // 關聯的消息隊列// 確保主線程 Looper 不會被 GC 回收(Binder 機制相關)Binder.clearCallingIdentity();final long ident = Binder.clearCallingIdentity();for (;;) { // 無限循環,直到隊列退出Message msg = queue.next(); // 取出消息(可能阻塞)if (msg == null) { // 隊列退出(msg.next == null)return; // 退出循環}// 處理消息:通過 msg.target(Handler)分發msg.target.dispatchMessage(msg);// 回收消息到池中(非必須,系統自動處理)msg.recycleUnchecked();}
}
2.?MessageQueue.next () 的阻塞與喚醒
next()
?是消息循環的核心,通過 native 層實現阻塞等待:
Message next() {final long ptr = mPtr; // 指向 native 層的 MessageQueue 實例int pendingIdleHandlerCount = -1; // 首次調用時初始化為 -1int nextPollTimeoutMillis = 0;for (;;) {if (nextPollTimeoutMillis != 0) {Binder.flushPendingCommands();}// native 層阻塞等待消息,直到有消息或被喚醒nativePollOnce(ptr, nextPollTimeoutMillis);synchronized (this) {// 檢查是否有消息待處理final long now = SystemClock.uptimeMillis();Message prevMsg = null;Message msg = mMessages;if (msg != null && msg.when > now) { // 消息未到執行時間,計算延遲nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);} else { // 有可執行的消息,取出頭節點msg = mMessages;mMessages = msg.next;msg.next = null;if (DEBUG) Log.v(TAG, "Returning message: " + msg);nextPollTimeoutMillis = -1; // 下次立即 poll}if (msg != null) { // 有消息,返回給 Looperreturn msg;}// 無消息,檢查是否退出mQuitting = true;return null;}}
}
- 阻塞原理:通過?
nativePollOnce
?進入內核等待,當消息入隊時(enqueueMessage
?中調用?nativeWake
)被喚醒。 - 退出條件:調用?
Looper.quit()
?或?Looper.quitSafely()
?時,mQuitting
?設為 true,next()
?返回 null,loop()
?終止。
四、消息處理:Handler.dispatchMessage 的分發邏輯
消息最終由發送它的 Handler 處理,分發流程如下:
public void dispatchMessage(Message msg) {if (msg.callback != null) { // 優先處理 Message 的 Runnable(post(Runnable))handleCallback(msg);} else if (mCallback != null) { // 其次處理 Handler 的 Callbackif (mCallback.handleMessage(msg)) {return;}}handleMessage(msg); // 最后調用用戶重寫的 handleMessage()
}private static void handleCallback(Message message) {message.callback.run(); // 執行 post(Runnable) 傳入的 Runnable
}
- 三種處理方式:
- Message 自帶的 Runnable:通過?
post(Runnable)
?發送的消息,直接執行?Runnable.run()
。 - Handler 的 Callback:通過構造函數傳入的?
Callback
,優先級高于?handleMessage
。 - 用戶重寫的 handleMessage:最常用的消息處理邏輯。
- Message 自帶的 Runnable:通過?
五、子線程使用 Handler 的完整流程(源碼級示例)
// 子線程類
class WorkerThread extends Thread {private Handler mHandler;private Looper mLooper;// 獲取 Handler(供外部發送消息)public Handler getHandler() {return mHandler;}@Overridepublic void run() {// 1. 準備 Looper(創建 Looper 和 MessageQueue)Looper.prepare();synchronized (this) {mLooper = Looper.myLooper(); // 保存當前線程的 LoopermHandler = new Handler() { // 創建 Handler 關聯當前 Looper@Overridepublic void handleMessage(Message msg) {// 處理消息(子線程中執行)processMessage(msg);if (msg.what == MSG_QUIT) { // 退出消息mLooper.quit(); // 停止消息循環}}};notify(); // 通知主線程 Handler 已準備好}// 2. 啟動消息循環Looper.loop();}
}// 主線程使用子線程 Handler
public class MainActivity extends AppCompatActivity {private WorkerThread mWorkerThread;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);mWorkerThread = new WorkerThread();mWorkerThread.start();synchronized (mWorkerThread) {try {mWorkerThread.wait(); // 等待子線程準備好 Handler} catch (InterruptedException e) {e.printStackTrace();}}// 向子線程發送消息Message msg = Message.obtain();msg.what = WorkerThread.MSG_WORK;mWorkerThread.getHandler().sendMessage(msg);// 發送退出消息mWorkerThread.getHandler().sendMessage(Message.obtain(null, WorkerThread.MSG_QUIT));}
}
- 關鍵步驟:
- 子線程中調用?
Looper.prepare()
?創建 Looper 和 MessageQueue。 - 創建 Handler 時自動關聯當前 Looper(因在?
prepare()
?之后調用)。 - 調用?
Looper.loop()
?啟動消息循環,處理外部發送的消息。 - 通過?
Looper.quit()
?終止循環,避免子線程阻塞。
- 子線程中調用?
六、常見問題源碼級解析
1.?為什么主線程可以直接創建 Handler?
- 主線程(ActivityThread 所在線程)在啟動時,框架自動調用了:
public static void main(String[] args) {// ...Looper.prepareMainLooper(); // 準備主線程 LooperActivityThread thread = new ActivityThread();thread.attach(false, startSeq);Looper.loop(); // 啟動主線程消息循環 }
因此主線程的 Looper 已存在,無需手動調用?prepare()
。
2.?MessageQueue 真的是 “隊列” 嗎?
- 從數據結構看,它是一個?單鏈表,而非傳統的 FIFO 隊列。消息按?
msg.when
?排序插入,保證按時間順序執行,支持延遲消息。
3.?postDelayed 不準時的根本原因?
postDelayed
?計算延遲的基準時間是?SystemClock.uptimeMillis()
(系統啟動后非休眠時間),若設備休眠,休眠時間不計入延遲,導致實際執行時間晚于預期。- 此外,消息需等待前面的消息處理完成,若主線程被阻塞(如耗時操作),延遲消息會被阻塞。
4.?多線程發送消息到同一 MessageQueue 為何線程安全?
MessageQueue.enqueueMessage
?使用?synchronized (this)
?加鎖,確保同一時間只有一個線程操作隊列,避免并發問題。
七、Handler 機制的設計精髓
- 線程隔離:通過 ThreadLocal 保證每個線程獨有 Looper 和 MessageQueue,避免資源競爭。
- 消息池:重用 Message 對象,減少內存分配和 GC 壓力,提升性能。
- 有序調度:按時間排序的消息鏈表,支持延遲和異步消息,靈活控制執行順序。
- 阻塞與喚醒:通過 native 層實現高效的等待與喚醒,避免 CPU 空轉。
總結
? ? ?Handler 機制的核心是通過?Looper(消息循環)、MessageQueue(有序消息鏈表)、Handler(消息收發器)?的協作,實現線程間的安全通信。源碼中大量使用 ThreadLocal、synchronized、單鏈表等技術,確保線程隔離、數據安全和性能優化。深入理解這些細節,能幫助開發者更好地處理線程通信、避免內存泄漏(如非靜態內部類 Handler 導致的 Activity 泄漏),并在復雜場景中靈活運用 Handler 機制。