Handler工作流程
📌 面试重要度:⭐⭐⭐⭐⭐
考察频率:字节 98% | 阿里 95% | 腾讯 96%
一、核心概念
1.1 定义与作用
一句话定义: Handler 工作流程是指从消息发送到最终处理的完整过程,包括消息入队、Looper 循环取消息、消息分发、消息处理四个核心阶段,通过 Handler、Message、MessageQueue、Looper 四个类的协作完成线程间通信。
为什么重要:
- 面试必考:几乎所有 Android 面试都会问到 Handler 工作流程,是检验对 Android 消息机制理解深度的标准
- 系统核心:Android 主线程的事件驱动模型基于 Handler 实现,生命周期回调、事件分发、View 绘制都依赖这套机制
- 调试关键:理解工作流程才能定位 ANR、内存泄漏、消息丢失等问题
- 源码分析基础:是深入学习 Android Framework 的入口,涉及 Java 层和 Native 层交互
1.2 与其他概念的关系
Handler 工作流程建立在四大核心类(Handler、Message、MessageQueue、Looper)的基础之上(详见 ./01-Handler基本概念.md),本文聚焦于完整的消息流转过程。具体的 API 使用方式(sendMessage、post 等)已在 ./02-Handler使用方式.md 中详细说明,本文从源码角度分析底层实现原理。
二、核心原理
2.1 完整工作流程概览
Handler 工作流程分为四个核心阶段:
┌─────────────────────────────────────────────────────────┐
│ Handler 工作流程 │
└─────────────────────────────────────────────────────────┘
阶段1:消息发送
┌──────────────┐
│ Handler │ handler.sendMessage(msg)
│ sendMessage()│────┐
└──────────────┘ │
↓
阶段2:消息入队
┌─────────────────────────────┐
│ MessageQueue │
│ enqueueMessage() │
│ 按时间排序插入单链表 │
│ [msg1]→[msg2]→[msg3] │
└─────────────────────────────┘
│
↓
阶段3:消息循环与取出
┌─────────────────────────────┐
│ Looper.loop() │
│ for(;;) { │
│ msg = queue.next() ←─── │ 阻塞等待(epoll)
│ if (msg == null) return │
│ msg.target.dispatch() │
│ } │
└─────────────────────────────┘
│
↓
阶段4:消息分发与处理
┌─────────────────────────────┐
│ Handler.dispatchMessage() │
│ 1. msg.callback? │
│ 2. mCallback? │
│ 3. handleMessage() │
└─────────────────────────────┘
时序图:
线程A(发送者) MessageQueue 线程B(Looper所在线程)
│ │ │
│ sendMessage(msg) │ │
│──────────────────────→│ │
│ │ enqueueMessage() │
│ │ (插入链表) │
│ │ │
│ │ │ Looper.loop()
│ │ │ for(;;)
│ │←───────────────────────│ queue.next()
│ │ │
│ │ (阻塞等待) │ nativePollOnce()
│ │ │
│ 新消息到达 │ │
│──────────────────────→│ │
│ │ nativeWake() │
│ │───────────────────────→│ 唤醒
│ │ │
│ │───────────────────────→│ 返回 Message
│ │ │
│ │ │ msg.target.dispatch()
│ │ │ handleMessage(msg)
│ │ │
│ │ │ msg.recycle()
│ │ │
│ │←───────────────────────│ queue.next()
│ │ │ (继续循环)
2.2 阶段1:消息发送
2.2.1 发送流程源码分析
所有消息发送方法最终都会调用 sendMessageAtTime():
// frameworks/base/core/java/android/os/Handler.java (Android 12)
public final boolean sendMessage(@NonNull Message msg) {
return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
// ↑ 关键:计算绝对执行时间
}
public boolean sendMessageAtTime(@NonNull 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(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
msg.target = this; // ← 关键1:设置消息的目标 Handler
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true); // ← 关键2:异步消息标记
}
return queue.enqueueMessage(msg, uptimeMillis); // ← 关键3:插入消息队列
}
关键步骤:
- 时间计算:
delayMillis相对时间转换为uptimeMillis绝对时间 - target 绑定:
msg.target = this,确保消息回到正确的 Handler - 异步标记:如果 Handler 是异步的,标记 Message 为异步消息
- 入队操作:调用
MessageQueue.enqueueMessage()插入队列
2.3 阶段2:消息入队
2.3.1 MessageQueue 入队机制
MessageQueue 本质是按时间排序的单链表。消息入队的核心逻辑:
关键步骤:
- 线程安全检查:使用
synchronized (this)保证线程安全 - 状态验证:检查消息是否已使用、队列是否已退出
- 时间排序插入:遍历链表找到合适位置,按
when升序排列 - 唤醒 Looper:新消息插入队列头部时,通过
nativeWake()唤醒阻塞的 Looper
唤醒条件:
- 队列原本为空,新消息到达
- 新消息的执行时间早于队列头部消息
- 存在同步屏障,且新消息是异步消息
详细源码分析:详见
../03-MessageQueue队列管理/01-消息入队enqueueMessage.md
2.4 阶段3:消息循环与取出
2.4.1 Looper.loop() 主循环
// frameworks/base/core/java/android/os/Looper.java (Android 12)
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;
// 无限循环,除非调用 quit()
for (;;) {
Message msg = queue.next(); // ← 关键:阻塞式取消息
if (msg == null) {
return; // 队列退出,结束循环
}
try {
msg.target.dispatchMessage(msg); // ← 关键:分发消息
} finally {
msg.recycleUnchecked(); // ← 关键:回收消息
}
}
}
核心流程:
- 获取 Looper:通过
myLooper()获取当前线程的 Looper - 无限循环:
for(;;)持续运行,直到队列退出 - 阻塞取消息:
queue.next()阻塞等待,直到有消息或队列退出返回 null - 消息分发:
msg.target.dispatchMessage(msg)将消息分发给对应的 Handler - 消息回收:
msg.recycleUnchecked()回收消息到对象池复用
详细源码实现:详见
../02-Looper循环/02-Looper.loop()源码分析.md
2.4.2 MessageQueue.next() 阻塞机制
// frameworks/base/core/java/android/os/MessageQueue.java (Android 12)
Message next() {
final long ptr = mPtr;
if (ptr == 0) {
return null; // MessageQueue 已销毁
}
int nextPollTimeoutMillis = 0; // 下次阻塞时长
for (;;) {
// ← 关键:Native 层阻塞等待
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message msg = mMessages;
// 处理同步屏障(跳过同步消息,找异步消息)
if (msg != null && msg.target == null) {
do {
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// 消息未到时间,计算阻塞时长
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// 消息到时间,从队列移除并返回
mMessages = msg.next;
msg.next = null;
msg.markInUse();
return msg; // ← 返回消息给 Looper
}
} else {
// 队列为空,无限阻塞
nextPollTimeoutMillis = -1;
}
// 队列退出
if (mQuitting) {
dispose();
return null;
}
}
// 队列空闲时执行 IdleHandler(省略详细代码)
// ...
}
}
核心逻辑:
- Native 层阻塞:调用
nativePollOnce()进入阻塞等待 - 同步屏障处理:队列头是同步屏障时,跳过同步消息找异步消息
- 时间判断:
- 消息到时间 → 返回消息
- 消息未到时间 → 计算阻塞时长
- 队列为空 → 无限阻塞(-1)
- 空闲处理:队列空闲时执行 IdleHandler 回调
阻塞时长计算:
| 场景 | nextPollTimeoutMillis | 含义 |
|---|---|---|
| 队列为空 | -1 | 无限阻塞,直到新消息到达 |
| 有延迟消息 | msg.when - now | 阻塞到消息执行时间 |
| 有即时消息 | 0 | 不阻塞,立即返回 |
详细源码分析:详见
../03-MessageQueue队列管理/02-消息出队next.mdIdleHandler 机制:详见../05-IdleHandler/IdleHandler原理.md
Native 层阻塞机制(epoll):
MessageQueue 的阻塞与唤醒底层使用 Linux epoll 机制实现:
nativePollOnce() → Native Looper → epoll_wait(阻塞等待)
↓
管道有数据写入
↓
立即唤醒返回
核心原理:
- epoll_wait 阻塞:等待文件描述符事件,支持超时参数
- 管道通信:MessageQueue 创建时建立管道(pipe)用于唤醒
- nativeWake 唤醒:向管道写入数据,epoll_wait 立即返回
epoll 详细实现:详见
../03-MessageQueue队列管理/04-epoll机制.md
2.5 阶段4:消息分发与处理
2.5.1 Handler.dispatchMessage() 三级分发
// frameworks/base/core/java/android/os/Handler.java (Android 12)
public void dispatchMessage(@NonNull Message msg) {
// 优先级1:Message 自带的 callback(post 方式)
if (msg.callback != null) {
handleCallback(msg);
} else {
// 优先级2:Handler 的 mCallback
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return; // 返回 true 则拦截
}
}
// 优先级3:Handler 子类重写的 handleMessage()
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
message.callback.run(); // 直接执行 Runnable
}
// 子类重写
public void handleMessage(@NonNull Message msg) {
}
分发优先级示意图:
dispatchMessage(msg)
│
├─→ msg.callback != null?
│ ├─ YES → handleCallback(msg) → msg.callback.run()
│ └─ NO ↓
│
├─→ mCallback != null?
│ ├─ YES → mCallback.handleMessage(msg)
│ │ ├─ return true → 拦截,结束
│ │ └─ return false → 继续 ↓
│ └─ NO ↓
│
└─→ handleMessage(msg) → 子类重写的处理逻辑
三种处理方式对比:
| 优先级 | 来源 | 触发方式 | 使用场景 |
|---|---|---|---|
| 1 | msg.callback | handler.post(Runnable) | 简单一次性任务 |
| 2 | mCallback | new Handler(Callback) | 拦截处理、统一日志 |
| 3 | handleMessage() | 继承 Handler 重写 | 复杂消息处理 |
2.6 重要细节与边界条件
细节1:主线程 Looper 何时创建
// frameworks/base/core/java/android/app/ActivityThread.java (Android 12)
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
// ...
Looper.prepareMainLooper(); // ← 创建主线程 Looper
// ...
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
// ...
Looper.loop(); // ← 开启主线程消息循环
throw new RuntimeException("Main thread loop unexpectedly exited");
}
关键点:
- 应用启动时,
ActivityThread.main()自动创建主线程 Looper - 主线程 Looper 不允许退出(
quitAllowed = false) - 如果主线程 Looper 退出,会抛出 RuntimeException 导致应用崩溃
细节2:同步屏障(Sync Barrier)机制
// frameworks/base/core/java/android/os/MessageQueue.java (Android 12)
public int postSyncBarrier() {
return postSyncBarrier(SystemClock.uptimeMillis());
}
private int postSyncBarrier(long when) {
synchronized (this) {
final int token = mNextBarrierToken++;
final Message msg = Message.obtain();
msg.markInUse();
msg.when = when;
msg.arg1 = token;
Message prev = null;
Message p = mMessages;
if (when != 0) {
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
}
if (prev != null) {
msg.next = p;
prev.next = msg;
} else {
msg.next = p;
mMessages = msg;
}
return token;
}
}
同步屏障特点:
- target 为 null:普通消息的
target指向 Handler,同步屏障的target为 null - 阻塞同步消息:MessageQueue.next() 会跳过所有同步消息,只处理异步消息
- 应用场景:View 绘制时,ViewRootImpl 会插入同步屏障,优先处理 VSYNC 信号
为什么应用层无法使用:
postSyncBarrier()是@hide方法,应用层无法直接调用- 滥用会导致同步消息饿死,引发 ANR
详细原理:详见
../06-同步屏障/同步屏障原理.md
细节3:IdleHandler 空闲消息处理
// frameworks/base/core/java/android/os/MessageQueue.java (Android 12)
public static interface IdleHandler {
boolean queueIdle(); // 返回 false 则执行一次后移除,返回 true 则保留
}
public void addIdleHandler(@NonNull IdleHandler handler) {
if (handler == null) {
throw new NullPointerException("Can't add a null IdleHandler");
}
synchronized (this) {
mIdleHandlers.add(handler);
}
}
执行时机:
- 队列为空
- 队列头部消息未到执行时间
应用场景:
- Activity 启动优化:在空闲时预加载资源
- 性能监控:统计主线程空闲时间
详细原理:详见
../05-IdleHandler/IdleHandler原理.md
三、实际应用
3.1 典型场景
场景1:主线程事件驱动模型
Android 主线程的所有任务都通过 Handler 消息机制执行:
// frameworks/base/core/java/android/app/ActivityThread.java (Android 12)
class H extends Handler {
public static final int LAUNCH_ACTIVITY = 100;
public static final int PAUSE_ACTIVITY = 101;
public static final int PAUSE_ACTIVITY_FINISHING= 102;
public static final int STOP_ACTIVITY_SHOW = 103;
public static final int STOP_ACTIVITY_HIDE = 104;
public static final int SHOW_WINDOW = 105;
public static final int HIDE_WINDOW = 106;
public static final int RESUME_ACTIVITY = 107;
// ...
public void handleMessage(Message msg) {
switch (msg.what) {
case LAUNCH_ACTIVITY: {
final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
r.loadedApk = getLoadedApkNoCheck(...);
handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
break;
}
case RESUME_ACTIVITY: {
handleResumeActivity((IBinder) msg.obj, true, msg.arg1 != 0, true, ...);
break;
}
// ...
}
}
}
系统事件通过 Handler 分发:
| 事件类型 | what 值 | 触发时机 | 处理方法 |
|---|---|---|---|
| 启动 Activity | LAUNCH_ACTIVITY | AMS 调用 | handleLaunchActivity() |
| 恢复 Activity | RESUME_ACTIVITY | AMS 调用 | handleResumeActivity() |
| 暂停 Activity | PAUSE_ACTIVITY | AMS 调用 | handlePauseActivity() |
| 停止 Activity | STOP_ACTIVITY | AMS 调用 | handleStopActivity() |
场景2:View 绘制流程中的同步屏障
// frameworks/base/core/java/android/view/ViewRootImpl.java (Android 12)
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
// 插入同步屏障,优先处理 VSYNC 信号
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
// 请求 VSYNC 信号(异步消息)
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
}
}
void unscheduleTraversals() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
// 移除同步屏障
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
mChoreographer.removeCallbacks(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
}
}
同步屏障作用:
- 插入同步屏障后,所有同步消息被阻塞
- VSYNC 信号到达后,异步消息(绘制任务)优先执行
- 绘制完成后移除同步屏障,恢复同步消息处理
场景3:IdleHandler 实现启动优化
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 主线程空闲时预加载资源
Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
@Override
public boolean queueIdle() {
// 预加载图片
preloadImages();
// 初始化第三方 SDK
initThirdPartySdk();
return false; // 返回 false,执行一次后移除
}
});
}
private void preloadImages() {
// 预加载逻辑
}
private void initThirdPartySdk() {
// 初始化逻辑
}
}
3.2 最佳实践
✅ 推荐做法
1. 理解消息流转过程,避免误用
// ✅ 正确:在主线程更新 UI
Handler mainHandler = new Handler(Looper.getMainLooper());
new Thread(() -> {
// 耗时操作
String result = loadData();
// 切换到主线程
mainHandler.post(() -> {
textView.setText(result);
});
}).start();
2. 利用 IdleHandler 优化启动性能
// ✅ 推荐:非紧急任务延迟到空闲时执行
Looper.myQueue().addIdleHandler(() -> {
initNonCriticalComponents();
return false;
});
3. 监控主线程消息处理时长
// ✅ 推荐:通过 Looper.setMessageLogging() 监控 ANR
Looper.getMainLooper().setMessageLogging(new Printer() {
private static final long BLOCK_THRESHOLD = 100; // 100ms
private long startTime;
@Override
public void println(String x) {
if (x.startsWith(">>>>> Dispatching")) {
startTime = SystemClock.uptimeMillis();
} else if (x.startsWith("<<<<< Finished")) {
long duration = SystemClock.uptimeMillis() - startTime;
if (duration > BLOCK_THRESHOLD) {
Log.w("Looper", "Message processing took " + duration + "ms");
}
}
}
});
❌ 常见错误
错误1:在主线程执行耗时操作
// ❌ 错误
Handler handler = new Handler(Looper.getMainLooper());
handler.post(() -> {
String data = loadDataFromNetwork(); // 网络请求,耗时操作
textView.setText(data); // 导致 ANR
});
// ✅ 正确
new Thread(() -> {
String data = loadDataFromNetwork();
handler.post(() -> {
textView.setText(data);
});
}).start();
错误2:误解 Looper.loop() 是死循环会卡死主线程
// ❌ 错误理解
// "Looper.loop() 是死循环,会卡死主线程"
// ✅ 正确理解
// Looper.loop() 虽然是死循环,但会通过 epoll 机制阻塞等待,
// 不会占用 CPU 资源,不会卡死主线程。
// 主线程必须持续运行,所有事件都通过消息机制处理。
错误3:忘记移除消息导致内存泄漏
// ❌ 错误
handler.postDelayed(() -> {
// 延迟60秒执行
}, 60000);
// Activity 销毁时未移除,导致内存泄漏
// ✅ 正确
@Override
protected void onDestroy() {
super.onDestroy();
handler.removeCallbacksAndMessages(null);
}
3.3 性能优化建议
1. 避免频繁发送消息
// ❌ 不推荐
for (int i = 0; i < 1000; i++) {
handler.sendEmptyMessage(i);
}
// ✅ 推荐:合并消息
handler.removeMessages(MSG_UPDATE);
handler.sendEmptyMessage(MSG_UPDATE);
2. 使用 IdleHandler 延迟非紧急任务
// ✅ 推荐
Looper.myQueue().addIdleHandler(() -> {
// 空闲时执行
updateCache();
return false;
});
3. 监控消息处理时长,定位 ANR
// ✅ 推荐:使用 BlockCanary 等工具监控主线程卡顿
四、面试真题解析
4.1 基础必答题(P5必须掌握)
【高频题1】描述 Handler 的完整工作流程
标准答案(30秒): Handler 工作流程分为四个阶段:第一阶段是消息发送,Handler 调用 sendMessage 最终会调用 enqueueMessage 将消息插入 MessageQueue;第二阶段是消息入队,MessageQueue 按时间排序将消息插入单链表,如果 Looper 阻塞且新消息插入队列头部会唤醒 Looper;第三阶段是消息循环,Looper.loop 无限循环调用 MessageQueue.next 取消息,如果队列为空会通过 epoll 机制阻塞等待;第四阶段是消息分发,Looper 将消息分发给 msg.target 对应的 Handler,Handler 调用 dispatchMessage 经过三级分发最终执行 handleMessage 处理消息。
深入展开(追问后):
四个阶段详细流程:
- 消息发送阶段:
handler.sendMessage(msg)
→ sendMessageDelayed(msg, 0)
→ sendMessageAtTime(msg, SystemClock.uptimeMillis() + delay)
→ enqueueMessage(queue, msg, uptimeMillis)
├─ msg.target = this // 设置目标 Handler
└─ queue.enqueueMessage(msg, uptimeMillis)
- 消息入队阶段:
MessageQueue.enqueueMessage(msg, when)
├─ msg.when = when
├─ 遍历链表,按时间排序插入
└─ if (needWake) nativeWake(mPtr) // 唤醒 Looper
- 消息循环阶段:
Looper.loop()
for (;;) {
msg = queue.next() // 阻塞式取消息
├─ if (队列为空) nativePollOnce(-1) // 无限阻塞
├─ if (延迟消息) nativePollOnce(delay) // 定时阻塞
└─ return msg
msg.target.dispatchMessage(msg)
msg.recycleUnchecked()
}
- 消息分发阶段:
Handler.dispatchMessage(msg)
if (msg.callback != null)
handleCallback(msg) // 优先级1
else if (mCallback != null && mCallback.handleMessage(msg))
return // 优先级2,返回 true 则拦截
else
handleMessage(msg) // 优先级3
面试官追问:
追问1:MessageQueue 是队列吗?为什么叫 Queue?
答:MessageQueue 不是真正的队列,而是按时间排序的单链表。之所以叫 Queue,是因为它提供了类似队列的 FIFO 语义:先发送的消息(when 相同时)先执行。但由于支持延迟消息,必须按时间排序,所以底层使用单链表实现,插入时间复杂度 O(n),取消息时间复杂度 O(1)。
追问2:Looper.loop() 是死循环,为什么不会 ANR?
答:Looper.loop() 虽然是死循环,但不会导致 ANR,原因有三点:
- epoll 阻塞机制:队列为空时,MessageQueue.next() 调用 nativePollOnce() 进入阻塞状态,释放 CPU 资源,不会空转
- 事件驱动模型:Android 主线程必须持续运行,所有 UI 事件、生命周期回调都通过消息机制触发
- 单个消息执行时间短:ANR 是单个消息处理超过 5 秒导致的,不是 Looper 循环导致的
实际 ANR 场景:
// ❌ 会 ANR
handler.post(() -> {
Thread.sleep(6000); // 单个消息处理超过 5 秒
});
// ✅ 不会 ANR
for (int i = 0; i < 1000; i++) {
handler.post(() -> {
// 每个消息处理 10ms,总共 10 秒也不会 ANR
});
}
【高频题2】MessageQueue 如何实现阻塞和唤醒?
标准答案(30秒): MessageQueue 通过 Linux epoll 机制实现阻塞和唤醒。当队列为空或队列头部消息未到执行时间时,MessageQueue.next 会调用 nativePollOnce 进入阻塞状态,底层使用 epoll_wait 监听管道的读端。当有新消息插入队列头部时,enqueueMessage 会调用 nativeWake,向管道的写端写入数据,epoll_wait 监听到读端有数据立即返回,Looper 被唤醒继续取消息。这种机制避免了 CPU 空转,提高了性能。
深入展开(追问后):
epoll 机制原理:
- 创建管道:
// frameworks/base/core/jni/android_os_MessageQueue.cpp
NativeMessageQueue::NativeMessageQueue() {
mLooper = Looper::getForThread();
if (mLooper == NULL) {
mLooper = new Looper(false);
Looper::setForThread(mLooper);
}
}
// system/core/libutils/Looper.cpp
Looper::Looper(bool allowNonCallbacks) {
// 创建管道
int wakeFds[2];
int result = pipe(wakeFds);
mWakeReadPipeFd = wakeFds[0];
mWakeWritePipeFd = wakeFds[1];
// 创建 epoll 实例
mEpollFd = epoll_create(EPOLL_SIZE_HINT);
// 监听管道读端
struct epoll_event eventItem;
eventItem.events = EPOLLIN;
eventItem.data.fd = mWakeReadPipeFd;
epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem);
}
- 阻塞等待:
int Looper::pollInner(int timeoutMillis) {
// 使用 epoll_wait 阻塞等待
int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
if (eventCount == 0) {
return POLL_TIMEOUT; // 超时
}
for (int i = 0; i < eventCount; i++) {
int fd = eventItems[i].data.fd;
uint32_t epollEvents = eventItems[i].events;
if (fd == mWakeReadPipeFd) {
if (epollEvents & EPOLLIN) {
awoken(); // 被唤醒,读取管道数据
}
}
}
return POLL_WAKE;
}
void Looper::awoken() {
uint64_t counter;
TEMP_FAILURE_RETRY(read(mWakeReadPipeFd, &counter, sizeof(uint64_t)));
}
- 唤醒机制:
void Looper::wake() {
uint64_t inc = 1;
// 向管道写端写入数据
ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeWritePipeFd, &inc, sizeof(uint64_t)));
}
阻塞时长计算表:
| 队列状态 | nextPollTimeoutMillis | epoll_wait 行为 |
|---|---|---|
| 队列为空 | -1 | 无限阻塞,直到 nativeWake() |
| 有延迟消息(未到时间) | msg.when - now | 定时阻塞,到时间自动返回 |
| 有即时消息 | 0 | 立即返回,不阻塞 |
面试官追问:
追问1:为什么不用 Object.wait() 和 notify()?
答:主要有三个原因:
- 跨进程唤醒:epoll 可以监听文件描述符,支持跨进程唤醒(例如 Binder 驱动写入数据唤醒应用进程)
- 精确定时:epoll_wait 支持超时参数,可以精确控制阻塞时长
- 统一机制:Native Looper 和 Java Looper 都使用 epoll,保持一致性
追问2:nativeWake() 会不会重复唤醒?
答:不会。nativeWake() 向管道写入数据,awoken() 会读取管道数据清空,即使多次调用 nativeWake(),epoll_wait 也只会返回一次。类似于信号量的设计,多次 wake 等同于一次 wake。
【高频题3】Handler 的消息分发机制是什么?
标准答案(30秒): Handler 消息分发采用三级分发机制。Looper 取出消息后调用 msg.target.dispatchMessage,第一优先级检查 msg.callback 是否为空,如果不为空直接执行 Runnable.run,这是 post 方式的消息;第二优先级检查 Handler 的 mCallback 是否为空,如果不为空调用 mCallback.handleMessage,返回 true 则拦截不再继续分发;第三优先级调用 Handler 子类重写的 handleMessage 方法。这种设计提供了灵活的消息处理机制,既支持简单的 Runnable 任务,又支持复杂的消息处理逻辑。
深入展开(追问后):
三级分发源码:
// frameworks/base/core/java/android/os/Handler.java (Android 12)
public void dispatchMessage(@NonNull Message msg) {
// 优先级1:Message.callback (Runnable)
if (msg.callback != null) {
handleCallback(msg);
} else {
// 优先级2:Handler.mCallback
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return; // 返回 true 拦截
}
}
// 优先级3:handleMessage()
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
三种使用方式示例:
// 优先级1:post 方式
handler.post(() -> {
Log.d("Handler", "优先级1:msg.callback");
textView.setText("更新 UI");
});
// 优先级2:Callback 拦截
Handler handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
Log.d("Handler", "优先级2:mCallback");
// 统一日志处理
if (msg.what == MSG_ERROR) {
handleError(msg);
return true; // 拦截,不再调用 handleMessage
}
return false; // 继续分发
}
});
// 优先级3:子类重写
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
Log.d("Handler", "优先级3:handleMessage");
// 复杂消息处理
switch (msg.what) {
case MSG_UPDATE:
// ...
break;
}
}
};
面试官追问:
追问1:为什么 mCallback 可以拦截 handleMessage?
答:这是责任链模式的变体。通过返回值控制是否继续传递消息,提供了灵活的扩展机制。典型应用场景:
- 统一拦截处理:全局日志记录、性能监控
- 条件处理:根据消息类型决定是否继续处理
- 测试 Mock:测试时替换处理逻辑,无需修改 Handler 子类
追问2:post 的 Runnable 在哪个线程执行?
答:Runnable 在 Handler 绑定的 Looper 所在线程执行,与调用 post 的线程无关。
Handler mainHandler = new Handler(Looper.getMainLooper());
// 子线程调用 post
new Thread(() -> {
mainHandler.post(() -> {
// 这里在主线程执行
Log.d("Thread", Thread.currentThread().getName()); // main
});
}).start();
原理:post 底层是 sendMessage,消息插入主线程 MessageQueue,主线程 Looper 取出后在主线程执行。
【高频题4】主线程的 Looper 何时创建?如何保证只有一个?**
标准答案(30秒): 主线程的 Looper 在应用启动时由 ActivityThread.main 方法自动创建。main 方法调用 Looper.prepareMainLooper 创建主线程 Looper,然后调用 Looper.loop 开启消息循环。Looper 通过 ThreadLocal 保证每个线程只有一个 Looper,prepareMainLooper 内部会检查 sThreadLocal.get 是否为空,如果已存在则抛出异常。主线程 Looper 创建时设置 quitAllowed 为 false,不允许退出,如果主线程 Looper 退出会抛出 RuntimeException 导致应用崩溃。
深入展开(追问后):
主线程 Looper 创建源码:
// frameworks/base/core/java/android/app/ActivityThread.java (Android 12)
public static void main(String[] args) {
// ...
Looper.prepareMainLooper(); // ← 创建主线程 Looper
// ...
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
// ...
Looper.loop(); // ← 开启主线程消息循环
throw new RuntimeException("Main thread loop unexpectedly exited");
}
prepareMainLooper 源码:
// frameworks/base/core/java/android/os/Looper.java (Android 12)
public static void prepareMainLooper() {
prepare(false); // quitAllowed = false
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
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 原理:
// frameworks/base/core/java/android/os/Looper.java (Android 12)
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
public static @Nullable Looper myLooper() {
return sThreadLocal.get(); // 获取当前线程的 Looper
}
ThreadLocal 内部机制:
- 每个 Thread 对象都有一个
ThreadLocalMap - ThreadLocal 作为 key,Looper 作为 value
- 不同线程的 Map 互不影响,实现线程隔离
面试官追问:
追问1:如果调用 Looper.getMainLooper().quit() 会怎样?
答:会抛出异常。主线程 Looper 创建时设置了 quitAllowed = false:
// frameworks/base/core/java/android/os/Looper.java (Android 12)
public void quit() {
mQueue.quit(false);
}
// frameworks/base/core/java/android/os/MessageQueue.java (Android 12)
void quit(boolean safe) {
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
// ...
}
追问2:子线程如何创建 Looper?
答:子线程需要手动调用 Looper.prepare() 和 Looper.loop():
new Thread(() -> {
Looper.prepare(); // 1. 创建 Looper
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
// 处理消息
}
};
Looper.loop(); // 2. 开启消息循环
}).start();
或者使用 HandlerThread:
HandlerThread thread = new HandlerThread("WorkThread");
thread.start();
Handler handler = new Handler(thread.getLooper());
【高频题5】Message 的复用机制是什么?**
标准答案(30秒): Message 使用享元模式实现对象复用,内部维护了一个最大容量为 50 的对象池。调用 Message.obtain 时会从对象池的链表头部取出一个 Message 复用,如果池为空则创建新对象。消息处理完毕后 Looper 会自动调用 recycleUnchecked 方法清空消息的所有字段数据,然后放回对象池链表头部。这种机制避免了频繁创建销毁 Message 对象,减少 GC 压力,提升性能。开发中应该优先使用 Message.obtain 而非 new Message。
深入展开(追问后):
对象池源码:
// frameworks/base/core/java/android/os/Message.java (Android 12)
private static Message sPool; // 对象池链表头节点
private static int sPoolSize = 0; // 当前池大小
private static final int MAX_POOL_SIZE = 50; // 最大容量
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() {
// 清空所有字段
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = UID_NONE;
workSourceUid = UID_NONE;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool; // 放回池中
sPool = this;
sPoolSize++;
}
}
}
对象池结构:
sPool → [Message1] → [Message2] → [Message3] → ... → [Message50] → null
(最新回收) (最早回收)
obtain() 从头部取出
recycle() 插入到头部
Looper 自动回收:
// frameworks/base/core/java/android/os/Looper.java (Android 12)
public static void loop() {
for (;;) {
Message msg = queue.next();
// ...
try {
msg.target.dispatchMessage(msg);
} finally {
msg.recycleUnchecked(); // ← 自动回收
}
}
}
面试官追问:
追问1:为什么对象池最大容量是 50?
答:这是性能和内存的平衡:
- 避免内存占用过大:Message 可能携带大量数据(Bundle、Bitmap 等),无限制缓存会占用过多内存
- 满足高频场景:50 个对象足够应对大部分高频消息场景
- 超过容量自动回收:超过 50 个的 Message 不会放回池中,由 GC 回收
追问2:为什么 recycle 要清空所有字段?
答:防止内存泄漏。Message 可能持有 Activity、View、Bitmap 等大对象的引用,如果不清空直接放回对象池,即使外部不再使用该 Message,这些对象也无法被 GC 回收,导致内存泄漏。
示例:
// 错误:未清空会导致泄漏
Message msg = Message.obtain();
msg.obj = bitmap; // 持有 Bitmap 引用
handler.sendMessage(msg);
// msg 处理完后如果不清空 obj,bitmap 无法回收
// 正确:recycleUnchecked 会清空 obj
msg.recycleUnchecked();
// obj = null,bitmap 可以被 GC 回收
4.2 进阶加分题(P6/P6+)
【进阶题1】同步屏障(Sync Barrier)的工作原理是什么?**
参考答案:
同步屏障是一种特殊的 Message,它的 target 字段为 null(普通消息的 target 指向 Handler)。当 MessageQueue 中存在同步屏障时,MessageQueue.next() 会跳过所有同步消息,只处理异步消息,从而实现异步消息的优先执行。
插入同步屏障:
// frameworks/base/core/java/android/os/MessageQueue.java (Android 12)
public int postSyncBarrier() {
return postSyncBarrier(SystemClock.uptimeMillis());
}
private int postSyncBarrier(long when) {
synchronized (this) {
final int token = mNextBarrierToken++;
final Message msg = Message.obtain();
msg.markInUse();
msg.when = when;
msg.arg1 = token;
// ← 关键:不设置 target,target 为 null
// 按时间顺序插入链表
Message prev = null;
Message p = mMessages;
if (when != 0) {
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
}
if (prev != null) {
msg.next = p;
prev.next = msg;
} else {
msg.next = p;
mMessages = msg;
}
return token;
}
}
跳过同步消息:
// frameworks/base/core/java/android/os/MessageQueue.java (Android 12)
Message next() {
for (;;) {
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
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) {
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// 返回异步消息
return msg;
}
}
}
}
}
应用场景:View 绘制:
// frameworks/base/core/java/android/view/ViewRootImpl.java (Android 12)
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
// 插入同步屏障
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
// 请求 VSYNC 信号(异步消息)
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
}
}
void unscheduleTraversals() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
// 移除同步屏障
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
}
}
追问:为什么应用层无法使用同步屏障?
答:postSyncBarrier() 和 removeSyncBarrier() 都是 @hide 方法,应用层无法直接调用。原因:
- 风险高:同步屏障会阻塞所有同步消息,滥用会导致主线程任务饿死,引发 ANR
- 系统级优化:只应该在可控场景下使用,例如 View 绘制、输入事件处理
- 需要成对使用:必须及时移除同步屏障,否则会永久阻塞同步消息
【进阶题2】IdleHandler 的工作原理和应用场景?**
参考答案:
IdleHandler 是 MessageQueue 提供的一种回调接口,当消息队列空闲时(队列为空或队列头部消息未到执行时间)会执行 IdleHandler.queueIdle() 方法。
IdleHandler 接口:
// frameworks/base/core/java/android/os/MessageQueue.java (Android 12)
public static interface IdleHandler {
boolean queueIdle(); // 返回 false 执行一次后移除,返回 true 保留
}
public void addIdleHandler(@NonNull IdleHandler handler) {
if (handler == null) {
throw new NullPointerException("Can't add a null IdleHandler");
}
synchronized (this) {
mIdleHandlers.add(handler);
}
}
执行时机:
// frameworks/base/core/java/android/os/MessageQueue.java (Android 12)
Message next() {
for (;;) {
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// ...
if (msg != null) {
if (now < msg.when) {
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
return msg;
}
} else {
nextPollTimeoutMillis = -1;
}
// ← IdleHandler 执行时机:队列空闲
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// 执行 IdleHandler
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null;
boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
}
}
应用场景:
- Activity 启动优化:
Looper.myQueue().addIdleHandler(() -> {
// 预加载资源
preloadImages();
// 初始化第三方 SDK
initSDK();
return false; // 执行一次后移除
});
- 性能监控:
Looper.myQueue().addIdleHandler(() -> {
long idleTime = SystemClock.uptimeMillis() - lastBusyTime;
Log.d("Performance", "Main thread idle for " + idleTime + "ms");
return true; // 保留,持续监控
});
追问:IdleHandler 和 postDelayed(0) 有什么区别?
答:
| 对比项 | IdleHandler | postDelayed(0) |
|---|---|---|
| 执行时机 | 队列空闲时 | 立即插入队列,按顺序执行 |
| 优先级 | 低于所有消息 | 与其他即时消息相同 |
| 适用场景 | 非紧急任务、启动优化 | 切换到主线程执行 |
| 可能不执行 | 队列一直繁忙可能不执行 | 一定会执行 |
【进阶题3】如何监控主线程消息处理时长定位 ANR?**
参考答案:
可以通过 Looper.setMessageLogging() 监控主线程消息处理时长,定位 ANR 原因。
原理:
// frameworks/base/core/java/android/os/Looper.java (Android 12)
public static void loop() {
final Looper me = myLooper();
final MessageQueue queue = me.mQueue;
for (;;) {
Message msg = queue.next();
if (msg == null) {
return;
}
final Printer logging = me.mLogging; // ← 日志打印器
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
try {
msg.target.dispatchMessage(msg);
} finally {
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
}
msg.recycleUnchecked();
}
}
监控实现:
public class LooperMonitor {
private static final long BLOCK_THRESHOLD = 100; // 100ms 阈值
public static void start() {
Looper.getMainLooper().setMessageLogging(new Printer() {
private long startTime;
private boolean isDispatching;
@Override
public void println(String x) {
if (x.startsWith(">>>>> Dispatching")) {
startTime = SystemClock.uptimeMillis();
isDispatching = true;
} else if (x.startsWith("<<<<< Finished")) {
if (isDispatching) {
long duration = SystemClock.uptimeMillis() - startTime;
if (duration > BLOCK_THRESHOLD) {
// 打印堆栈
StackTraceElement[] stackTrace = Looper.getMainLooper().getThread().getStackTrace();
StringBuilder sb = new StringBuilder();
for (StackTraceElement element : stackTrace) {
sb.append(element.toString()).append("\n");
}
Log.w("LooperMonitor", "Message processing took " + duration + "ms\n" + sb.toString());
}
isDispatching = false;
}
}
}
});
}
}
开源方案:BlockCanary:
public class BlockCanary {
public void start() {
Looper.getMainLooper().setMessageLogging(mainLooperPrinter);
}
class LooperPrinter implements Printer {
@Override
public void println(String x) {
if (x.startsWith(">>>>> Dispatching")) {
startDump();
} else if (x.startsWith("<<<<< Finished")) {
stopDump();
}
}
}
}
追问:这种监控方案的缺点是什么?
答:
- 性能开销:每个消息都会调用 println,频繁打印日志影响性能
- 无法监控 Native 层:只能监控 Java 层消息,Native 层耗时无法感知
- 堆栈不准确:打印堆栈时消息可能已经处理完毕,堆栈信息不准确
更好的方案:
- Choreographer.FrameCallback:监控帧率,超过 16.6ms 即为卡顿
- 插桩:编译时插桩,精确记录方法耗时
- TraceView/Systrace:系统工具,更全面的性能分析
4.3 实战场景题
【场景题】主线程 Handler 大量消息堆积导致 UI 卡顿,如何优化?**
场景描述: 一个列表页面滑动时会发送大量刷新消息到主线程 Handler,导致消息队列堆积,UI 卡顿。如何优化?
问题分析:
- 根本原因:频繁发送消息导致 MessageQueue 堆积,单个消息处理时间短但总量大
- 卡顿表现:列表滑动不流畅,帧率下降
- 优化目标:减少消息数量,提高处理效率
解决方案:
方案1:合并消息(推荐)
private Handler handler = new Handler(Looper.getMainLooper());
private static final int MSG_REFRESH = 1;
public void requestRefresh() {
// 移除之前的刷新消息,只保留最新的
handler.removeMessages(MSG_REFRESH);
handler.sendEmptyMessageDelayed(MSG_REFRESH, 16); // 延迟 16ms,合并一帧内的请求
}
方案2:使用 IdleHandler 延迟非紧急任务
Looper.myQueue().addIdleHandler(() -> {
// 空闲时执行非紧急刷新
updateNonCriticalUI();
return false;
});
方案3:分批处理
private List<Item> pendingItems = new ArrayList<>();
public void addItem(Item item) {
pendingItems.add(item);
if (pendingItems.size() >= 10) {
processBatch();
}
}
private void processBatch() {
handler.post(() -> {
adapter.addAll(pendingItems);
pendingItems.clear();
});
}
方案4:异步处理 + 结果回调
ExecutorService executor = Executors.newSingleThreadExecutor();
public void loadData() {
executor.execute(() -> {
// 子线程处理数据
List<Item> items = processData();
// 主线程更新 UI
handler.post(() -> {
adapter.setItems(items);
});
});
}
追问:
1. 方案缺点?
-
方案1(合并消息):
- 优点:简单有效,减少消息数量
- 缺点:可能丢失部分刷新请求,适合最终状态一致的场景
-
方案2(IdleHandler):
- 优点:不占用主线程繁忙时间
- 缺点:主线程一直繁忙可能不执行
-
方案3(分批处理):
- 优点:减少频率,提高效率
- 缺点:延迟更新,用户体验稍差
-
方案4(异步处理):
- 优点:耗时操作在子线程
- 缺点:增加线程切换开销
2. 如何选择方案?
| 场景 | 推荐方案 |
|---|---|
| 频繁刷新同一数据 | 方案1(合并消息) |
| 非紧急任务 | 方案2(IdleHandler) |
| 大量数据更新 | 方案3(分批处理) |
| 耗时操作 | 方案4(异步处理) |
3. 如何监控优化效果?
// 使用 Choreographer 监控帧率
Choreographer.getInstance().postFrameCallback(new Choreographer.FrameCallback() {
private long lastFrameTime;
@Override
public void doFrame(long frameTimeNanos) {
long currentTime = frameTimeNanos / 1000000;
if (lastFrameTime != 0) {
long frameDuration = currentTime - lastFrameTime;
if (frameDuration > 16) { // 超过 16ms 即为掉帧
Log.w("FPS", "Frame drop: " + frameDuration + "ms");
}
}
lastFrameTime = currentTime;
Choreographer.getInstance().postFrameCallback(this);
}
});
五、对比与总结
5.1 关键阶段对比
| 阶段 | 核心类 | 关键方法 | 主要逻辑 |
|---|---|---|---|
| 消息发送 | Handler | sendMessageAtTime() | 计算 when,设置 target |
| 消息入队 | MessageQueue | enqueueMessage() | 按时间排序插入链表,唤醒 Looper |
| 消息循环 | Looper | loop() | 无限循环,阻塞取消息 |
| 消息取出 | MessageQueue | next() | epoll 阻塞等待,返回消息 |
| 消息分发 | Handler | dispatchMessage() | 三级分发机制 |
| 消息处理 | Handler | handleMessage() | 子类重写处理逻辑 |
| 消息回收 | Message | recycleUnchecked() | 清空字段,放回对象池 |
5.2 核心要点速记
一句话记忆: Handler 通过四大核心类协作完成消息从发送、入队、循环取出、分发处理到回收的完整流程,底层通过 epoll 机制实现高效阻塞唤醒,支持线程间通信和延迟任务。
3个关键点:
- 消息入队:MessageQueue 按时间排序的单链表,插入时唤醒阻塞的 Looper
- 消息循环:Looper.loop() 无限循环通过 epoll 阻塞等待,避免 CPU 空转
- 消息分发:Handler 三级分发机制(msg.callback → mCallback → handleMessage)
面试官最爱问:
- 完整流程:发送 → 入队 → 循环 → 分发 → 处理 → 回收
- 阻塞唤醒:epoll_wait 阻塞,nativeWake 写管道唤醒
- 线程切换:Handler 绑定 Looper,消息在 Looper 线程执行
六、关联知识点
前置知识:
- Handler 四大核心类(Handler、Message、MessageQueue、Looper)(详见:
./01-Handler基本概念.md) - Handler 使用方式(sendMessage、post 等 API)(详见:
./02-Handler使用方式.md)
后续扩展:
- MessageQueue 同步屏障机制详解
- IdleHandler 原理与应用
- Choreographer VSYNC 机制
- HandlerThread 源码实现
- AsyncTask 底层原理
相关文件:
./01-Handler基本概念.md- Handler 定义、四大核心类关系、基本工作机制./02-Handler使用方式.md- sendMessage、post、postDelayed 等 API 详解