Handler消息机制
# 03.Handler消息机制
# 目录介绍
- 一、引言:为什么需要Handler
- 二、Handler机制的整体架构
- 2.1 四大核心组件
- 2.2 消息流转的完整过程
- 三、MessageQueue的数据结构与实现
- 3.1 消息队列的链表结构
- 3.2 消息入队(enqueueMessage)
- 3.3 消息出队(next)
- 四、Looper的工作原理
- 4.1 Looper的创建与准备
- 4.2 ThreadLocal的工作原理
- 4.3 Looper.loop():消息循环
- 五、Handler的消息发送与处理
- 5.1 消息发送的多种方式
- 5.2 消息分发优先级
- 5.3 Handler与Looper的绑定关系
- 六、Message的复用池机制
- 6.1 Message的对象池实现
- 6.2 复用池的设计思想
- 七、Native层的MessageQueue
- 7.1 Java层与Native层的关系
- 7.2 NativeMessageQueue的创建
- 八、epoll机制与线程休眠
- 8.1 为什么使用epoll
- 8.2 nativePollOnce的实现
- 8.3 nativeWake的实现
- 九、同步屏障与异步消息
- 9.1 什么是同步屏障
- 9.2 同步屏障在ViewRootImpl中的应用
- 9.3 同步屏障的实现原理
- 十、IdleHandler机制
- 10.1 IdleHandler的定义与使用
- 10.2 IdleHandler在系统中的应用
- 10.3 IdleHandler的执行时机
- 十一、HandlerThread与IntentService
- 11.1 HandlerThread的实现
- 11.2 IntentService的原理(已废弃但值得了解)
- 十二、Handler导致的内存泄漏
- 12.1 泄漏的根本原因
- 12.2 解决方案
- 十三、主线程Looper为什么不会ANR
- 13.1 常见误解
- 13.2 正确理解
- 13.3 ANR的检测机制
- 十四、面试高频问题与深度分析
- 14.1 一个线程可以有几个Handler?几个Looper?几个MessageQueue?
- 14.2 Handler的postDelayed是精确的吗?
- 14.3 子线程能更新UI吗?
- 十五、子线程消息处理
- 15.1 子线程中定义Handler
- 15.2 子线程更新UI方式
- 15.3 Handler内存泄漏
- 15.4 消息Delay可靠性
- 15.5 IdleHandler空闲机制
- 15.6 Looper停止与App退出
- 十六、总结
# 一、引言:为什么需要Handler
Android的UI框架采用单线程模型——只有主线程(UI线程)可以更新界面。这个设计简化了UI操作的线程安全问题,但带来了一个挑战:耗时操作(网络请求、数据库读写、文件IO)不能在主线程执行(否则ANR),处理完结果后又需要切回主线程更新UI。
疑惑:为什么Android不采用多线程直接更新UI的方式?
答疑:如果允许多线程同时修改UI,就需要对所有UI操作加锁,这会带来两个严重问题:
- 性能下降:每次UI操作都需要获取锁,频繁的锁竞争会严重影响渲染性能
- 死锁风险:复杂的UI操作涉及多个View层级,多线程加锁容易产生死锁
Handler机制正是为了解决线程间通信问题而设计的。它提供了一种优雅的方式,让任意线程都能向目标线程的消息队列投递消息,由目标线程按序处理。
# 二、Handler机制整体架构
# 2.1 四大核心组件
Handler消息机制的四大组件:
┌──────────────────────────────────────────────────┐
│ Thread │
│ ┌──────────┐ ┌──────────────┐ │
│ │ Handler │───→│ MessageQueue │ │
│ │ 消息处理器 │ │ 消息队列 │ │
│ └──────────┘ │ ┌──────────┐ │ ┌──────────┐ │
│ ↑ │ │ Message │ │ │ Looper │ │
│ │ │ │ Message │←│──│ 消息循环 │ │
│ │ │ │ Message │ │ │ │ │
│ 处理消息 │ └──────────┘ │ └──────────┘ │
│ └──────────────┘ │
└──────────────────────────────────────────────────┘
2
3
4
5
6
7
8
9
10
11
12
Message :消息载体,携带what、arg1、arg2、obj等数据
Handler :消息的发送者和处理者
MessageQueue:消息的存储容器(按时间排序的链表)
Looper :消息循环,不断从MessageQueue中取消息交给Handler处理
# 2.2 消息流转的完整过程
子线程 主线程
│ │
│ handler.sendMessage(msg) │
│─────────────────────────→│ msg加入MessageQueue
│ │ (按msg.when排序插入链表)
│ │
│ │ Looper.loop()
│ │ ├── MessageQueue.next()
│ │ │ └── 取出队首消息
│ │ ├── msg.target.dispatchMessage(msg)
│ │ │ └── handler.handleMessage(msg)
│ │ └── msg.recycleUnchecked()
│ │
2
3
4
5
6
7
8
9
10
11
12
13
# 三、MessageQueue的数据结构与实现
# 3.1 消息队列的链表结构
MessageQueue并不是Java标准的Queue,而是一个按时间排序的单链表:
// MessageQueue.java
public final class MessageQueue {
Message mMessages; // 链表头指针(最早需要处理的消息)
// 链表结构示意:
// mMessages → msg1(when=100) → msg2(when=200) → msg3(when=500) → null
// ↑最先处理 ↑其次 ↑最后
}
2
3
4
5
6
7
8
# 3.2 消息入队(enqueueMessage)
boolean enqueueMessage(Message msg, long when) {
synchronized (this) {
msg.when = when;
Message p = mMessages; // 当前队列头
boolean needWake;
// 情况1:队列为空,或新消息的when最小(需要最先处理)
if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg; // 新消息成为队列头
needWake = mBlocked; // 如果线程在阻塞,需要唤醒
} else {
// 情况2:按时间顺序插入到链表的合适位置
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break; // 找到插入位置
}
}
msg.next = p;
prev.next = msg; // 插入到prev和p之间
}
// 唤醒阻塞在nativePollOnce的线程
if (needWake) {
nativeWake(mPtr); // 通过eventfd唤醒
}
}
return true;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 3.3 消息出队(next)
Message next() {
int pendingIdleHandlerCount = -1;
int nextPollTimeoutMillis = 0;
for (;;) {
// 阻塞等待(核心!)
// nextPollTimeoutMillis = -1:无限等待直到被唤醒
// nextPollTimeoutMillis = 0:立即返回
// nextPollTimeoutMillis > 0:等待指定时间
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
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) {
// 还没到处理时间,计算需要等待的时间
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// 取出消息
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
return msg; // 返回消息给Looper
}
} else {
nextPollTimeoutMillis = -1; // 无消息,无限等待
}
// 处理IdleHandler(消息队列空闲时执行)
if (pendingIdleHandlerCount < 0 && (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
// ... 执行IdleHandler
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# 四、Looper的工作原理
# 4.1 Looper的创建与准备
public final class Looper {
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<>();
private static Looper sMainLooper; // 主线程Looper
final MessageQueue mQueue;
final Thread mThread;
// 每个线程只能有一个Looper(通过ThreadLocal保证)
public static void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(true)); // 创建并存入ThreadLocal
}
// 主线程专用
public static void prepareMainLooper() {
prepare(false); // quitAllowed=false,主线程Looper不允许退出
sMainLooper = myLooper();
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 4.2 ThreadLocal的工作原理
ThreadLocal实现线程隔离的原理:
每个Thread对象内部都有一个ThreadLocalMap:
┌─────────────────────────────┐
│ Thread A │
│ threadLocals (ThreadLocalMap)│
│ ┌───────────┬──────┐ │
│ │ key=sThreadLocal │ value=LooperA │
│ └───────────┴──────┘ │
└─────────────────────────────┘
┌─────────────────────────────┐
│ Thread B │
│ threadLocals (ThreadLocalMap)│
│ ┌───────────┬──────┐ │
│ │ key=sThreadLocal │ value=LooperB │
│ └───────────┴──────┘ │
└─────────────────────────────┘
sThreadLocal.get() 实际是:
Thread.currentThread().threadLocals.get(sThreadLocal)
→ 不同线程获取到不同的Looper实例
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 4.3 Looper.loop():消息循环
public static void loop() {
final Looper me = myLooper();
final MessageQueue queue = me.mQueue;
// 确保线程身份是正确的
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) { // 无限循环
// 1. 从消息队列取消息(可能阻塞)
Message msg = queue.next();
if (msg == null) {
return; // MessageQueue.quit()被调用后返回null
}
// 2. 分发消息给对应的Handler
// msg.target就是发送这条消息的Handler
try {
msg.target.dispatchMessage(msg);
} finally {
// Trace结束
}
// 3. 检查Binder身份是否被篡改
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
// 日志警告:消息处理过程中Binder身份被改变
}
// 4. 回收消息到复用池
msg.recycleUnchecked();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 五、Handler的消息发送与处理
# 5.1 消息发送的多种方式
// 1. 发送Message
handler.sendMessage(msg);
handler.sendMessageDelayed(msg, delayMillis);
handler.sendMessageAtTime(msg, uptimeMillis);
handler.sendEmptyMessage(what);
// 2. 发送Runnable(内部也是封装为Message)
handler.post(runnable);
handler.postDelayed(runnable, delayMillis);
handler.postAtTime(runnable, uptimeMillis);
// 所有发送方式最终都汇聚到同一个入口:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this; // 将Handler自身设为消息的target
msg.workSourceUid = ThreadLocalWorkSource.getUid();
return queue.enqueueMessage(msg, uptimeMillis);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 5.2 消息分发优先级
public void dispatchMessage(Message msg) {
// 优先级1:Message自带的callback(通过post(Runnable)发送的)
if (msg.callback != null) {
handleCallback(msg); // 直接执行runnable.run()
return;
}
// 优先级2:Handler构造时传入的全局callback
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return; // callback返回true表示消息已处理
}
}
// 优先级3:Handler子类重写的handleMessage
handleMessage(msg);
}
// 分发优先级:
// msg.callback(Runnable)> Handler.Callback > handleMessage()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 5.3 Handler与Looper的绑定关系
// Handler必须绑定到一个Looper
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper; // 绑定的Looper
mQueue = looper.mQueue; // 绑定的MessageQueue
mCallback = callback;
mAsynchronous = async;
}
// 如果不指定Looper,默认绑定当前线程的Looper
public Handler() {
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
}
// 这就是为什么在子线程创建Handler需要先调用Looper.prepare()
// 也是为什么主线程可以直接创建Handler(主线程已自动调用prepareMainLooper)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 六、Message的复用池机制
# 6.1 Message的对象池实现
public final class Message implements Parcelable {
// 静态链表头:复用池
private static Message sPool;
private static int sPoolSize = 0;
private static final int MAX_POOL_SIZE = 50; // 最大缓存50个
Message next; // 链表指针(复用池和MessageQueue共用)
// 从复用池获取Message
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // 清除IN_USE标记
sPoolSize--;
return m; // 复用已回收的Message
}
}
return new Message(); // 池空则新建
}
// 回收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++;
}
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# 6.2 复用池的设计思想
Message复用池结构(链表实现的对象池):
sPool → [msg1] → [msg2] → [msg3] → null
↑ obtain()从头取出
↑ recycleUnchecked()从头插入
优势:
1. 减少GC压力:高频场景下避免大量创建/销毁Message对象
2. O(1)的获取和回收:链表头操作
3. 线程安全:synchronized保护
最佳实践:
// 推荐:从池中获取
Message msg = Message.obtain();
msg.what = MSG_UPDATE;
handler.sendMessage(msg);
// 不推荐:new创建(浪费对象)
Message msg = new Message();
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 七、Native层的MessageQueue
# 7.1 Java层与Native层的关系
Handler消息机制并非纯Java实现,底层依赖Native层:
Java层 Native层
┌──────────────────┐ ┌──────────────────┐
│ MessageQueue │ │ NativeMessageQueue│
│ ├── mPtr ────────│───────────→│ ├── mLooper │
│ ├── nativePollOnce│ │ │ (android::Looper)│
│ └── nativeWake │ │ └── pollOnce() │
└──────────────────┘ │ wake() │
└──────────────────┘
│
┌────┴────┐
│ epoll │
│(Linux) │
└─────────┘
2
3
4
5
6
7
8
9
10
11
12
13
# 7.2 NativeMessageQueue的创建
// frameworks/base/core/jni/android_os_MessageQueue.cpp
static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
return reinterpret_cast<jlong>(nativeMessageQueue);
}
NativeMessageQueue::NativeMessageQueue() {
mLooper = Looper::getForThread();
if (mLooper == NULL) {
mLooper = new Looper(false); // 创建Native Looper
Looper::setForThread(mLooper);
}
}
// Native Looper的构造
Looper::Looper(bool allowNonCallbacks) {
mWakeEventFd.reset(eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC));
// 创建eventfd用于唤醒
rebuildEpollLocked(); // 创建epoll实例
}
void Looper::rebuildEpollLocked() {
mEpollFd.reset(epoll_create1(EPOLL_CLOEXEC));
// 创建epoll实例
epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeEventFd, &wakeEvent);
// 将eventfd加入epoll监听
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 八、epoll机制与线程休眠
# 8.1 为什么使用epoll
Handler消息机制需要解决一个核心问题:当消息队列为空时,线程不能忙等待(浪费CPU),需要休眠;当有新消息到来时,需要能唤醒休眠的线程。
方案对比:
┌────────────┬──────────────────────────────────┐
│ 忙等待 │ while(!hasMessage) {} // CPU 100% │
│ sleep │ Thread.sleep(100) // 响应慢 │
│ wait/notify │ 需要额外的对象锁 // 可行但不够强大│
│ epoll │ 事件驱动,精确唤醒 // Android的选择 │
└────────────┴──────────────────────────────────┘
2
3
4
5
6
7
epoll的优势:
- 可以同时监听多个文件描述符(不仅是eventfd)
- 支持超时等待(精确到毫秒)
- 高效的事件通知(O(1)复杂度)
- 可以同时处理Native层的事件(如InputEvent、VSync等)
# 8.2 nativePollOnce的实现
// 这是MessageQueue.next()中阻塞的底层实现
void NativeMessageQueue::pollOnce(int timeoutMillis) {
mLooper->pollOnce(timeoutMillis);
}
int Looper::pollOnce(int timeoutMillis, ...) {
for (;;) {
// 先处理之前epoll_wait返回的Response
while (mResponseIndex < mResponses.size()) {
const Response& response = mResponses.itemAt(mResponseIndex++);
// 处理Native层的回调
}
result = pollInner(timeoutMillis);
if (result != POLL_CALLBACK) {
return result;
}
}
}
int Looper::pollInner(int timeoutMillis) {
// 核心:epoll_wait等待事件
int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
// timeoutMillis = -1 → 无限等待
// timeoutMillis = 0 → 立即返回
// timeoutMillis > 0 → 等待指定毫秒
for (int i = 0; i < eventCount; i++) {
if (eventItems[i].data.fd == mWakeEventFd) {
// 被nativeWake唤醒:读取eventfd清除唤醒标记
awoken();
} else {
// 其他fd的事件(如InputChannel、VSync等)
// 加入mResponses待处理
}
}
// 处理Native层注册的MessageHandler消息
while (mMessageEnvelopes.size() != 0) {
// ... 处理Native消息
}
return result;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# 8.3 nativeWake的实现
// 当新消息加入队列时,唤醒阻塞在epoll_wait的线程
void NativeMessageQueue::wake() {
mLooper->wake();
}
void Looper::wake() {
uint64_t inc = 1;
// 向eventfd写入一个值,触发epoll_wait返回
write(mWakeEventFd, &inc, sizeof(uint64_t));
}
// 流程:
// 线程A: handler.sendMessage(msg)
// → enqueueMessage()
// → nativeWake()
// → write(eventfd, 1)
// → epoll_wait()返回
// → 线程B从nativePollOnce()醒来
// → MessageQueue.next()取到新消息
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 九、同步屏障与异步消息
# 9.1 什么是同步屏障
同步屏障(Sync Barrier)是一种特殊的消息,它可以让MessageQueue优先处理异步消息,跳过普通的同步消息。这是Android UI渲染流程的关键机制。
正常消息处理顺序:
msg1(sync) → msg2(sync) → msg3(async) → msg4(sync)
按时间顺序依次处理:1, 2, 3, 4
设置同步屏障后:
barrier → msg1(sync) → msg2(sync) → msg3(async) → msg4(sync)
跳过sync消息,优先处理async消息:3
移除屏障后恢复正常:1, 2, 4
2
3
4
5
6
7
8
# 9.2 同步屏障在ViewRootImpl中的应用
// ViewRootImpl.java — scheduleTraversals
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
// 1. 设置同步屏障(确保UI绘制消息优先处理)
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
// 2. 发送异步消息(绘制回调)
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
// 这个消息被标记为异步消息
}
}
// 为什么需要同步屏障?
// VSync信号到来时,需要立即执行绘制操作
// 如果前面有很多同步消息在排队,绘制就会延迟,导致掉帧
// 同步屏障让绘制消息"插队",保证16ms内完成一帧渲染
void unscheduleTraversals() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
// 移除同步屏障
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 9.3 同步屏障的实现原理
// MessageQueue.java
public int postSyncBarrier() {
synchronized (this) {
final int token = mNextBarrierToken++;
final Message msg = Message.obtain();
msg.when = SystemClock.uptimeMillis();
// 关键:msg.target = null!
// 普通消息的target都是Handler,只有屏障消息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;
}
}
// MessageQueue.next()中的屏障处理逻辑:
// if (msg != null && msg.target == null) {
// // 遇到同步屏障,跳过同步消息,寻找异步消息
// do {
// prevMsg = msg;
// msg = msg.next;
// } while (msg != null && !msg.isAsynchronous());
// }
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# 十、IdleHandler机制
# 10.1 IdleHandler的定义与使用
// 当消息队列空闲时执行的回调
public static interface IdleHandler {
// 返回true:保持注册,下次空闲时继续执行
// 返回false:执行一次后自动移除
boolean queueIdle();
}
// 注册IdleHandler
Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
@Override
public boolean queueIdle() {
// 消息队列空闲时执行
// 适合做延迟初始化、数据预加载等
doSomeLazyInit();
return false; // 只执行一次
}
});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 10.2 IdleHandler在系统中的应用
系统使用IdleHandler的场景:
1. AMS — ActivityThread
→ 在消息队列空闲时执行GC(Runtime.getRuntime().gc())
→ 避免在业务处理时触发GC导致卡顿
2. Activity生命周期
→ onStop()的执行通过IdleHandler延迟
→ 只有消息队列空闲时才真正执行onStop
→ 这就是为什么onStop的调用时机不确定
3. LeakCanary(内存泄漏检测)
→ 在IdleHandler中检查弱引用是否被回收
→ 避免在业务繁忙时干扰正常运行
4. 应用启动优化
→ 将非关键初始化放在IdleHandler中
→ 保证首屏渲染不受影响
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 10.3 IdleHandler的执行时机
// MessageQueue.next()中IdleHandler的处理
Message next() {
for (;;) {
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// 取消息逻辑...
// 当没有消息需要立即处理时,检查IdleHandler
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
mBlocked = true;
continue; // 没有IdleHandler,继续阻塞
}
// 拷贝IdleHandler数组(避免持有锁时执行回调)
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// 在锁外执行IdleHandler
for (int i = 0; i < pendingIdleHandlerCount; i++) {
IdleHandler idler = mPendingIdleHandlers[i];
boolean keep = idler.queueIdle();
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler); // 移除一次性IdleHandler
}
}
}
pendingIdleHandlerCount = 0;
nextPollTimeoutMillis = 0; // IdleHandler可能产生新消息,重新检查
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# 十一、HandlerThread与IntentService
# 11.1 HandlerThread的实现
// HandlerThread:自带Looper的工作线程
public class HandlerThread extends Thread {
Looper mLooper;
@Override
public void run() {
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll(); // 通知getLooper()
}
onLooperPrepared(); // 钩子方法
Looper.loop(); // 进入消息循环
}
// 线程安全地获取Looper(可能需要等待线程启动完成)
public Looper getLooper() {
synchronized (this) {
while (mLooper == null) {
wait(); // 等待run()中Looper创建完成
}
}
return mLooper;
}
}
// 使用方式:
HandlerThread handlerThread = new HandlerThread("worker");
handlerThread.start();
Handler workHandler = new Handler(handlerThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
// 在工作线程中处理消息
}
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# 11.2 IntentService的原理(已废弃但值得了解)
// IntentService = HandlerThread + Service
public abstract class IntentService extends Service {
private volatile Looper mServiceLooper;
private volatile ServiceHandler mServiceHandler;
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
onHandleIntent((Intent) msg.obj); // 在工作线程处理
stopSelf(msg.arg1); // 处理完自动停止
}
}
@Override
public void onCreate() {
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg); // 转发到工作线程
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 十二、Handler导致的内存泄漏
# 12.1 泄漏的根本原因
内存泄漏链路:
非静态内部类Handler → 隐式持有外部Activity引用
↓
Handler被Message.target引用
↓
Message在MessageQueue中等待处理
↓
MessageQueue被Looper引用
↓
主线程Looper是静态的,永远不会被回收
↓
GC Root → Looper → MessageQueue → Message → Handler → Activity(泄漏!)
2
3
4
5
6
7
8
9
10
11
// 泄漏代码示例
public class LeakyActivity extends Activity {
// 非静态内部类:隐式持有LeakyActivity.this
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
updateUI(); // 引用了外部Activity的方法
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 发送一个10分钟后执行的延迟消息
mHandler.sendEmptyMessageDelayed(0, 10 * 60 * 1000);
// 如果Activity在10分钟内被销毁,Message仍在队列中
// Message持有Handler → Handler持有Activity → Activity泄漏
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 12.2 解决方案
// 方案1:静态内部类 + 弱引用
public class SafeActivity extends Activity {
private static class SafeHandler extends Handler {
private final WeakReference<SafeActivity> mActivity;
SafeHandler(SafeActivity activity) {
mActivity = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
SafeActivity activity = mActivity.get();
if (activity != null) {
activity.updateUI();
}
}
}
private final SafeHandler mHandler = new SafeHandler(this);
@Override
protected void onDestroy() {
super.onDestroy();
// 方案2:销毁时移除所有消息
mHandler.removeCallbacksAndMessages(null);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 十三、主线程Looper为什么不会ANR
# 13.1 常见误解
疑惑:主线程的Looper.loop()是一个死循环,为什么不会导致ANR?
答疑:这是一个非常常见的误解。首先需要明确ANR的定义:
ANR(Application Not Responding)的触发条件:
1. InputDispatching超时:5秒内未处理输入事件
2. Service超时:前台Service 20秒 / 后台Service 200秒
3. BroadcastReceiver超时:前台广播10秒 / 后台广播60秒
4. ContentProvider超时:10秒
ANR的本质是:某个消息(事件)在规定时间内没有被处理完
与Looper.loop()死循环无关!
2
3
4
5
6
7
8
9
# 13.2 正确理解
Looper.loop()的本质:
for (;;) {
Message msg = queue.next(); // ← 没有消息时在这里阻塞(epoll_wait)
msg.target.dispatchMessage(msg); // ← 处理消息
}
当没有消息时:
→ 线程阻塞在epoll_wait,CPU不占用,不耗电
→ 这不是ANR,这是正常的等待状态
→ 类似于你等电话:没电话时你在休息,不是"没有响应"
当有消息时:
→ 线程被唤醒,处理消息
→ 如果处理时间太长(比如在handleMessage里做了耗时操作)
→ 导致后续的输入事件、Service超时消息等无法及时处理
→ 这才触发ANR
类比:
Looper.loop() = 一个快递员不停取件送件的循环
没有快递时 = 快递员休息(阻塞等待),不是ANR
有快递但送不过来 = 快递延迟(消息处理慢),这才可能ANR
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 13.3 ANR的检测机制
ANR检测原理(以InputDispatching为例):
1. InputDispatcher发送输入事件给App
2. 同时启动一个5秒定时器
3. App处理完事件后回复InputDispatcher
4. 如果5秒内没有回复 → ANR
时序图:
InputDispatcher App主线程
│ │
│ dispatch event │
│──────────────────────→│
│ start 5s timer │ handleMessage() 处理中...
│ │ 处理中...
│ │ 处理中...
│ 5s timeout! │ 还在处理中...
│ → 触发ANR │
│ │
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 十四、面试高频问题与深度分析
# 14.1 一个线程可以有几个Handler?几个Looper?几个MessageQueue?
一个线程:
- 可以有多个Handler(随便创建)
- 只能有一个Looper(ThreadLocal保证)
- 只能有一个MessageQueue(Looper构造时创建)
多个Handler共享同一个MessageQueue:
Handler1.sendMessage(msg1) → msg1.target = Handler1
Handler2.sendMessage(msg2) → msg2.target = Handler2
两个消息都在同一个MessageQueue中
Looper取出msg1 → 调用Handler1.dispatchMessage()
Looper取出msg2 → 调用Handler2.dispatchMessage()
2
3
4
5
6
7
8
9
10
11
# 14.2 Handler的postDelayed是精确的吗?
不是精确的。原因:
1. 消息处理延迟
postDelayed(runnable, 1000)实际含义是:
"至少1000ms后处理",不是"恰好1000ms时处理"
如果队列前面的消息处理耗时500ms:
实际延迟 = 1000ms + 前面消息处理时间 ≈ 1500ms
2. 时间基准
使用的是SystemClock.uptimeMillis()(不计算休眠时间)
不是System.currentTimeMillis()(墙钟时间)
3. 系统调度
线程调度也有延迟(通常几毫秒到几十毫秒)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 14.3 子线程能更新UI吗?
严格来说:可以,但需要特定条件。
1. ViewRootImpl创建前(Activity.onResume之前)
→ 在onCreate中用子线程更新UI可能不报错
→ 因为checkThread()还未被建立
2. SurfaceView
→ 专门设计为可以在子线程绘制
→ 通过Surface的lockCanvas/unlockCanvasAndPost
3. 底层原理:
ViewRootImpl.checkThread():
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy "
+ "can touch its views.");
}
}
// mThread = 创建ViewRootImpl的线程(通常是主线程)
// 这不是"UI线程检查"而是"创建线程检查"
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 十五、子线程消息处理
# 15.1 子线程中定义Handler
直接在子线程中创建Handler会报错:Can't create handler inside thread that has not called Looper.prepare()。
这是因为Handler的工作依赖于Looper,而Looper通过ThreadLocal与特定线程绑定。主线程在ActivityThread.main()中已经调用了Looper.prepareMainLooper(),而子线程默认没有Looper。
正确做法:在子线程中先调用Looper.prepare(),再创建Handler。注意在所有事情完成后应该调用quit方法终止消息循环,否则子线程会一直处于等待状态。
# 15.2 子线程更新UI方式
主要有以下方法:
- 主线程中定义Handler,子线程通过mHandler发送消息,主线程Handler的handleMessage更新UI
- Activity对象的runOnUiThread方法
- 创建Handler,传入getMainLooper,使用handler.post更新UI
- View.post(Runnable r)
这些方法的实现原理本质上都是通过Handler发送消息来实现的。
# 15.3 Handler内存泄漏
泄漏原因:通过内部类方式创建的mHandler会隐式持有外部类(Activity)引用。当执行post/send方法时,Message持有Handler引用,Handler持有Activity引用。如果Activity退出时消息队列中还有未处理的消息,就会导致Activity无法被回收。
解决方案:
- 在Activity销毁时移除消息:
mHandler.removeCallbacksAndMessages(null) - 使用静态内部类+弱引用:将Handler改为匿名静态内部类,对Activity使用WeakReference
# 15.4 消息Delay可靠性
Handler的postDelayed不是精确的延时,不靠谱的原因:
- 发送的消息太多,Looper负载越高,任务越容易积压
- 消息队列中有耗时消息处理,导致后面的消息延时
- 对于时间精确度要求较高的场景,不应依赖Handler的delay
优化方案:
- 消息精简,减少数量
- 队列优化,重复消息过滤和互斥消息取消
- 复用消息(Message.obtain())
- 使用独享的Looper(HandlerThread)
# 15.5 IdleHandler空闲机制
IdleHandler是在消息队列空闲时执行的回调,适合处理不紧急的任务:
MessageQueue.IdleHandler idleHandler = new MessageQueue.IdleHandler() {
@Override
public boolean queueIdle() {
// 返回false表示执行一次后移除,返回true表示保留
return false;
}
};
Looper.myQueue().addIdleHandler(idleHandler);
2
3
4
5
6
7
8
# 15.6 Looper停止与App退出
调用Looper.getMainLooper().quit()或quitSafely()会导致App崩溃退出。
- quit():清空MessageQueue中的所有消息(包括延迟和非延迟消息)
- quitSafely():只清空延迟消息,非延迟消息会被派发处理后再退出
无论调用哪个方法,Looper退出后都不再接收新消息,通过Handler发送消息会返回false。
quit与quitSafely的源码级对比:
MessageQueue.quit(boolean safe) {
if (safe) {
// quitSafely:移除所有when > now的延迟消息
removeAllFutureMessagesLocked();
// 已到时间的消息正常处理完毕后退出
} else {
// quit:移除所有消息
removeAllMessagesLocked();
}
mQuitting = true;
nativeWake(mPtr); // 唤醒阻塞中的next()
}
MessageQueue.next() {
// ...
if (mQuitting) {
dispose();
return null; // 返回null给Looper
}
}
Looper.loop() {
Message msg = queue.next();
if (msg == null) {
return; // next返回null → 退出循环
}
}
主线程Looper退出的后果:
主线程的消息循环终止
→ 后续的生命周期回调无法执行
→ InputDispatcher发送的事件无人处理
→ 5秒后触发ANR
→ 最终进程被kill
子线程Looper退出是正常操作:
HandlerThread.quitSafely()
→ 处理完当前消息后退出
→ 线程结束运行
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# 十六、总结
Handler消息机制是Android最核心的线程通信框架:
Handler知识图谱:
核心组件
├── Message(消息载体) → 对象池复用
├── MessageQueue(消息队列) → 时间排序链表
├── Looper(消息循环) → ThreadLocal + 死循环
└── Handler(收发处理) → 发送 + 分发 + 处理
底层原理
├── Native层 → NativeMessageQueue + android::Looper
├── epoll机制 → 高效的事件等待与唤醒
├── eventfd → 线程间唤醒信号
└── 同步屏障 → 异步消息优先处理(UI渲染保障)
高级特性
├── IdleHandler → 空闲时执行
├── 同步屏障 → UI渲染优先级
├── HandlerThread → 自带Looper的工作线程
└── Message复用池 → 减少GC压力
常见问题
├── 内存泄漏 → 静态内部类 + 弱引用
├── ANR → 消息处理超时,不是Looper死循环
└── 精度问题 → postDelayed不精确
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
理解Handler机制,是理解Android UI渲染、事件分发、组件生命周期管理等高级主题的基础。它贯穿了Android应用开发的方方面面。