Android Handler 機制原理解析
一、 概述
Handler 機制是 Android 系統(tǒng)中實現(xiàn)**線程間通信(Inter-thread Communication)**的核心機制,尤其廣泛用于將子線程中的任務結果傳遞回主線程(UI線程)進行更新操作。其底層基于 消息隊列(MessageQueue) 和 循環(huán)器(Looper) 構建,形成一個典型的生產者-消費者模型。
該機制的核心目標是:
- 實現(xiàn)跨線程的消息傳遞
- 避免多線程并發(fā)修改 UI
- 提供延遲執(zhí)行和定時任務的能力
- 支持異步任務調度與回調處理
二、 核心組件
1. Handler(處理器)
Handler 是開發(fā)者最常接觸的類,負責:
- 發(fā)送消息:
sendMessage()、post()等方法 - 處理消息:通過重寫
handleMessage(Message msg)或設置Callback - 與特定線程的
Looper和MessageQueue綁定
?? 每個 Handler 必須關聯(lián)一個 Looper,否則會拋出異常。
// 示例:創(chuàng)建 Handler 并綁定當前線程的 Looper
Handler handler = new Handler(Looper.myLooper()) {
@Override
public void handleMessage(@NonNull Message msg) {
// 在指定線程中處理消息
}
};2. MessageQueue(消息隊列)
MessageQueue 是一個按時間排序的消息隊列,內部使用單向鏈表結構存儲待處理的消息。
主要職責:
- 存儲由
Handler發(fā)送的Message - 按
Message.when(執(zhí)行時間戳)排序插入 - 提供
next()方法供Looper取出下一條可執(zhí)行消息 - 支持阻塞等待(無消息時休眠,有消息時喚醒)
?? MessageQueue 并非傳統(tǒng) FIFO 隊列,而是根據執(zhí)行時間排序的優(yōu)先隊列。
3. Looper(循環(huán)器)
Looper 是消息循環(huán)的驅動者,每個線程最多只能有一個 Looper。
關鍵功能:
- 調用
Looper.prepare()初始化當前線程的Looper - 調用
Looper.loop()啟動無限循環(huán),不斷從MessageQueue取出消息并分發(fā) - 使用
ThreadLocal保證線程局部性
// 主線程中系統(tǒng)自動調用 Looper.prepareMainLooper(); // 初始化主線程 Looper ... Looper.loop(); // 開始循環(huán)
4. Message(消息)
Message 是消息的載體,包含以下核心字段:
| 字段 | 說明 |
|---|---|
what | 用戶自定義消息類型 |
arg1, arg2 | 整型參數,用于傳遞簡單數據 |
obj | 任意對象(注意內存泄漏風險) |
target | 目標 Handler,由 Handler.sendMessage() 自動設置 |
callback | 如果是 post(Runnable),則 Runnable 封裝為 callback |
when | 消息應被執(zhí)行的時間戳(毫秒) |
next | 指向鏈表中的下一個 Message |
推薦使用
Message.obtain()獲取實例,避免頻繁創(chuàng)建對象。
三、工作原理
1. 整體流程圖

2. 詳細執(zhí)行過程
1. 初始化 Looper(子線程示例)
在非主線程中若要使用 Handler,必須手動創(chuàng)建 Looper。
class WorkerThread extends Thread {
public Handler handler;
@Override
public void run() {
Looper.prepare(); // 創(chuàng)建當前線程的 Looper 和 MessageQueue
handler = new Handler(Looper.myLooper()) {
@Override
public void handleMessage(@NonNull Message msg) {
switch (msg.what) {
case 1:
Log.d("Handler", "收到消息: " + msg.obj);
break;
}
}
};
Looper.loop(); // 開始無限循環(huán)讀取消息
}
public void quit() {
if (handler != null) {
handler.getLooper().quit(); // 安全退出循環(huán)
}
}
}?? 注意:
Looper.prepare()只能調用一次,否則拋出RuntimeExceptionLooper.loop()是阻塞方法,直到調用quit()才退出
2. 消息發(fā)送機制
Handler 提供多種發(fā)送消息的方式:
| 方法 | 說明 |
|---|---|
sendEmptyMessage(int what) | 發(fā)送空消息 |
sendMessage(Message msg) | 發(fā)送自定義消息 |
post(Runnable r) | 發(fā)送 Runnable 任務 |
sendEmptyMessageDelayed(what, delay) | 延遲發(fā)送 |
postDelayed(Runnable r, long delayMillis) | 延遲執(zhí)行 Runnable |
底層均調用 enqueueMessage() 將消息插入 MessageQueue。
// 示例:多種發(fā)送方式
handler.sendEmptyMessage(1);
Message msg = Message.obtain();
msg.what = 2;
msg.obj = "Hello";
handler.sendMessage(msg);
handler.post(() -> {
Log.d("Handler", "Runnable 執(zhí)行");
});
handler.postDelayed(() -> {
Log.d("Handler", "1秒后執(zhí)行");
}, 1000);3. 消息處理流程
Handler.dispatchMessage() 是消息分發(fā)的核心邏輯:
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
// 優(yōu)先處理 post(Runnable) 類型的消息
handleCallback(msg);
} else {
if (mCallback != null) {
// 其次交給 Handler 的 Callback 處理
if (mCallback.handleMessage(msg)) {
return;
}
}
// 最終調用 handleMessage
handleMessage(msg);
}
}
private void handleCallback(Message message) {
message.callback.run(); // 直接執(zhí)行 Runnable
}執(zhí)行順序:Runnable > Callback > handleMessage
四、源碼分析(精簡版)
1. Looper 核心實現(xiàn)
public final class Looper {
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<>();
final MessageQueue mQueue;
final Thread mThread;
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
// 準備 Looper
public static void prepare() {
prepare(true);
}
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
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
// 消息循環(huán)主方法
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;
for (;;) {
Message msg = queue.next(); // 可能阻塞
if (msg == null) return; // 退出循環(huán)
// 分發(fā)消息
try {
msg.target.dispatchMessage(msg);
} finally {
// 空實現(xiàn),預留鉤子
}
msg.recycleUnchecked(); // 回收消息
}
}
}?? 關鍵點:
ThreadLocal保證每個線程獨享一個Looperloop()是死循環(huán),通過queue.next()阻塞等待消息msg.target即發(fā)送該消息的Handler
2. MessageQueue 關鍵方法
enqueueMessage()—— 入隊
boolean enqueueMessage(Message msg, long when) {
synchronized (this) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
final long now = SystemClock.uptimeMillis();
msg.when = when;
Message p = mMessages; // 鏈表頭節(jié)點
if (p == null || when == 0 || when < p.when) {
// 插入頭部
msg.next = p;
mMessages = msg;
} else {
// 按時間順序插入中間
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
}
msg.next = p;
prev.next = msg;
}
// 喚醒消息隊列(如果之前處于等待狀態(tài))
notify();
}
return true;
}next()—— 出隊(核心阻塞方法)
Message next() {
final long ptr = mPtr;
if (ptr == 0) return null; // 已退出
long pendingIdleHandlerCount = -1;
long nextPollTimeoutMillis = 0;
for (;;) {
// 阻塞等待,timeout 為 0 表示無限等待
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
// 同步屏障處理:跳過同步消息,只處理異步消息
if (msg != null && msg.target == null) {
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// 未到執(zhí)行時間,計算等待時間
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// 可執(zhí)行,取出消息
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
msg.markInUse();
return msg;
}
} else {
// 無消息,無限等待
nextPollTimeoutMillis = -1;
}
// 處理 IdleHandler(空閑時執(zhí)行)
if (pendingIdleHandlerCount < 0 && (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount > 0) {
// 執(zhí)行空閑任務
}
}
}
}五、高級特性
1. 同步屏障(Sync Barrier)
用于優(yōu)先處理異步消息(如 UI 繪制),屏蔽同步消息。
應用場景:
ViewRootImpl請求重繪時插入屏障,確保Choreographer的VSYNC消息優(yōu)先執(zhí)行
// 插入同步屏障 int token = mHandler.getLooper().getQueue().postSyncBarrier(); // 移除屏障 mHandler.getLooper().getQueue().removeSyncBarrier(token);
?? 濫用可能導致同步消息饑餓,不建議普通應用使用。
2. 空閑處理器(IdleHandler)
當 MessageQueue 沒有消息需要處理時,會回調 IdleHandler,可用于執(zhí)行低優(yōu)先級任務。
Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
@Override
public boolean queueIdle() {
Log.d("IdleHandler", "消息隊列空閑,執(zhí)行預加載");
// 返回 true:保持注冊;false:執(zhí)行一次后移除
return false;
}
});典型用途:
- 延遲初始化組件
- 預加載數據
- 執(zhí)行非關鍵任務以提升流暢度
3. 異步消息(Asynchronous Message)
避免被同步屏障阻塞,適用于高優(yōu)先級任務。
Message msg = Message.obtain(handler, runnable); msg.setAsynchronous(true); handler.sendMessageAtTime(msg, SystemClock.uptimeMillis() + 1000);
Handler.createAsync() 可創(chuàng)建專門處理異步消息的 Handler
六、內存優(yōu)化機制
1. Message 對象池(對象復用)
為減少頻繁創(chuàng)建/銷毀對象帶來的 GC 壓力,Message 內部維護了一個對象池。
public final class Message implements Parcelable {
private static final int MAX_POOL_SIZE = 50;
private static Message sPool;
private static int sPoolSize = 0;
private static final Object sPoolSync = new Object();
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();
}
public void recycle() {
if (isInUse()) return;
recycleUnchecked();
}
void recycleUnchecked() {
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
target = null;
callback = null;
data = null;
when = 0;
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
}建議始終使用 Message.obtain() 而非 new Message()
七、 使用注意事項
1. 避免內存泄漏
常見場景:非靜態(tài)內部類 Handler 持有外部 Activity 引用,導致無法回收。
解決方案:靜態(tài)內部類 + 弱引用
private static class SafeHandler extends Handler {
private final WeakReference<MainActivity> mActivity;
public SafeHandler(MainActivity activity) {
mActivity = new WeakReference<>(activity);
}
@Override
public void handleMessage(@NonNull Message msg) {
MainActivity activity = mActivity.get();
if (activity == null || activity.isFinishing() || activity.isDestroyed()) {
return;
}
switch (msg.what) {
case 1:
activity.updateUI((String) msg.obj);
break;
}
}
}或使用WeakHandler(第三方庫)
// 如 Square 的 LeakCanary 推薦方式
WeakHandler weakHandler = new WeakHandler(this, new Handler.Callback() {
@Override
public boolean handleMessage(@NonNull Message msg) {
// 安全處理
return true;
}
});2. 正確退出 Looper
在子線程中使用 Looper 后,應在適當時機退出,避免資源浪費。
// 安全退出:處理完已到時的消息再退出 handler.getLooper().quitSafely(); // 立即退出:丟棄所有未處理消息 handler.getLooper().quit();
建議在 Thread 的 onDestroy 或 quit() 方法中調用
3. 主線程 vs 子線程
| 線程類型 | Looper 是否自動創(chuàng)建 | 使用方式 |
|---|---|---|
| 主線程(UI線程) | 是(系統(tǒng)自動調用 Looper.prepareMainLooper()) | 直接創(chuàng)建 Handler |
| 子線程 | 否 | 必須手動調用 Looper.prepare() 和 Looper.loop() |
八、常見問題與使用建議
| 問題 | 解決方案 |
|---|---|
Can't create handler inside thread that has not called Looper.prepare() | 在子線程中先調用 Looper.prepare() |
| 內存泄漏 | 使用靜態(tài) Handler + WeakReference |
| 消息延遲不準 | 系統(tǒng)休眠或高負載可能導致延遲,不保證精確時間 |
| 大量消息堆積 | 控制消息頻率,避免 OOM |
使用 postDelayed 實現(xiàn)輪詢 | 不推薦,應使用 AlarmManager 或 WorkManager 替代 |
九、總結
Android 的 Handler 機制是一個精巧的線程通信架構,其設計體現(xiàn)了以下幾個關鍵思想:
| 組件 | 角色 | 設計亮點 |
|---|---|---|
Handler | 消息生產者與消費者 | 提供易用的 API |
MessageQueue | 消息存儲與調度 | 單鏈表 + 時間排序 + 阻塞喚醒機制 |
Looper | 消息循環(huán)驅動 | ThreadLocal + 無限循環(huán) |
Message | 消息載體 | 對象池復用 + 多種數據承載方式 |
1.核心優(yōu)勢
- 線程安全:保證 UI 更新在主線程完成
- 異步解耦:任務可在任意線程發(fā)起,在指定線程執(zhí)行
- 延遲執(zhí)行:支持定時任務和延遲操作
- 內存高效:
Message對象池減少 GC 壓力 - 擴展性強:支持同步屏障、空閑處理器等高級特性
2.應用場景
- 子線程更新 UI
- 延遲操作(如啟動頁倒計時)
- 定時任務(需結合
removeCallbacks防止泄漏) - 線程間狀態(tài)同步
?? 盡管現(xiàn)代 Android 開發(fā)越來越多地使用
Coroutine、LiveData、RxJava等替代方案,但Handler仍是底層基石,理解其原理有助于深入掌握 Android 消息機制。
到此這篇關于Android Handler 機制原理詳解的文章就介紹到這了,更多相關Android Handler 機制內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Android編程之基于Log演示一個activity生命周期實例詳解
這篇文章主要介紹了Android編程之基于Log演示一個activity生命周期,結合完整實例形式較為詳細的分析總結了Log演示activity生命周期的具體用法及Log的具體使用方法,需要的朋友可以參考下2015-12-12
Android編程創(chuàng)建與解析xml的常用方法詳解
這篇文章主要介紹了Android編程創(chuàng)建與解析xml的常用方法,結合具體實例形式較為詳細的分析了Android操作xml文件的步驟、實現(xiàn)技巧與相關注意事項,需要的朋友可以參考下2017-05-05
android開發(fā)中常用的Eclipse快捷鍵詳細整理
android開發(fā)中常用的Eclipse快捷鍵詳細整理方便查找,需要的朋友可以了解下2012-12-12

