先讲一下Handler到底是怎么跨线程的:
本质上,就是利用了同一进程中的线程间资源共享这一机制。例如,我们想在子线程完成一项任务后发送一个表示成功的消息到主线程。可以直接定义一个全局变量 public static volatile boolean message = false,然后等待子线程任务完成后给message赋值为true,这样主线程发现message的值为true就明白任务已经完成可以进行下一步操作了。这就是最简单的线程间消息传递了,当然为了主线程不至于傻等下去,我们得在主线程开启轮询不停的判断message的值是否为true,所以就有了Looper里面的无限for循环。由于同一时刻一个线程只能处理一个消息,为了应对多个线程同时发送多条消息的问题于是就有了messageQueue来按照时间顺序存放这些消息。不知道大家读到这里是否发现了根本没有Handler什么事,其实任何线程只要可以拿到messageQueue变量就可以把自己的消息添加到messageQueue,这事实上就完成了一次跨线程消息传递。但谷歌为了消息添加和消息处理的统一,专门构建了Handler类,message持有Handler的引用,哪个Handler发送最后就在哪个handler的回调方法中处理。
关于Looper核心逻辑总结:
- 线程(Thread)层面:
每个线程有一个ThreadLocalMap
,其中存储了多个线程本地变量(例如Looper
的sThreadLocal
)。 - ThreadLocal层面:
sThreadLocal
是全局唯一的键(Key),用于在每个线程的ThreadLocalMap
中存储当前线程的Looper
实例(Value)。 - Looper层面:
prepare()
方法通过检查null确保每个线程只存一个Looper
。Looper
在构造时创建唯一的MessageQueue
(mQueue
)。 - 结果:
每个线程 → 一个ThreadLocalMap → 一个Looper(通过sThreadLocal键) → 一个MessageQueue(通过mQueue字段)
一、主要涉及到的类和方法:
二、Message类
-
此类包含一个以sPool为头结点的单链表,链表最大长度为50
-
obtain()方法:从头结点取出一个message节点,sPool后移一位
if (sPool != null) { Message m = sPool;//局部变量保存头结点 sPool = m.next; //sPool指向新的节点(原头结点的next节点) m.next = null; //重置取出来的节点next指针 m.flags = 0; sPoolSize--; ////节点数量减1 return m; }
-
recycleUnchecked()方法:以头插法插入一个message节点:
if (sPoolSize < MAX_POOL_SIZE) {//MAX_POOL_SIZE = 50 next = sPool; //当前使用完毕的新节点指向sPoll(以头插法插入复用链表) sPool = this; //sPoll指向新的节点 sPoolSize++; //节点数量加1 }
三、enqueueMessage方法分析:
boolean enqueueMessage(Message msg, long when) {
msg.markInUse();
msg.when = when;
Message p = mMessages;//当前将要被处理的msg
boolean needWake;
if (p == null || when == 0 || when < p.when) {
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;
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
在if中,将待插入的message和当前将要被处理的msg比较,按照时间先后插入链表中。 else中,将待插入的msg和链表中元素依次比较,按照时间顺序插入其中。
四、MessageQueue类
-
quit(boolean safe) 方法的理解,直接看代码:
if (safe) { removeAllFutureMessagesLocked();//移除所有延迟消息 } else { removeAllMessagesLocked();//移除所有消息 }
-
IdleHandler:当线程将要进入堵塞,等待更多消息时,会回调这个接口。
public static interface IdleHandler { /** * Called when the message queue has run out of messages and will now * wait for more. Return true to keep your idle handler active, false * to have it removed. This may be called if there are still messages * pending in the queue, but they are all scheduled to be dispatched * after the current time. */ boolean queueIdle();//返回值为false,使用完后会在idleHandler集合中直接删除,反之则不会 }
使用方式:
// 监控帧率下降 Looper.myQueue().addIdleHandler { val frameTime: Long = SystemClock.uptimeMillis() - lastFrameTime if (frameTime > 32) { // 超过32ms/帧 reportJank(frameTime) } true // 持续监控 }
// 性能检测模板 void addInstrumentedIdleHandler(Runnable task) { Looper.myQueue().addIdleHandler(() -> { long start = SystemClock.uptimeMillis(); task.run(); long duration = SystemClock.uptimeMillis() - start; if (duration > 5) { logSlowTask(duration); } return false; }); }
在 Android 主线程中使用 IdleHandler 时,严格限制执行时间不超过 5 毫秒。这是基于 Android 框架设计的硬性要求:
时间范围 评级 对系统的影响 0-5ms ✅ 安全 无感知影响 5-16ms ⚠️ 警告 可能影响下一帧渲染 >16ms ❌ 危险 必定造成卡顿掉帧 - 一帧渲染周期 = 16ms (60FPS)
- 系统预留 10ms 给应用处理UI/逻辑
- IdleHandler 必须在 帧间空闲期 执行完毕
各场景耗时参考:
操作类型 平均耗时 备注 创建简单对象 0.01ms new Object() HashMap put(10次) 0.1ms 小数据操作安全 SharedPreferences读取 2~5ms 避免使用 View.invalidate() 0.5ms 简单视图 解析JSON(1KB) 1.5ms 接近临界值 SQLite查询(10条记录) 3~20ms 禁止在IdleHandler中使用 五、同步屏障机制
同步屏障其实是一条不持有handler引用的message,用于临时阻塞同步消息的处理,优先执行异步消息。可以通过postSyncBarrier()方法插入,移除同步屏障消息可以用removeSyncBarrier()方法。
在next()方法中先找到同步屏障消息即target为null,找到后循环找到第一条异步消息并处理。如果没有找到即msg==null则会进入阻塞,代码见下面:
if (msg != null && msg.target == null) { // Stalled by a barrier. Find the next asynchronous message in the queue. do { prevMsg = msg; msg = msg.next; } while (msg != null && !msg.isAsynchronous()); }
使用流程:
// 获取主线程消息队列 MessageQueue queue = Looper.getMainLooper().getQueue(); // 1. 插入同步屏障 int barrierToken = queue.postSyncBarrier(); // 2. 发送异步消息(高优先级) asyncHandler.sendEmptyMessage(MSG_CRITICAL_UPDATE); // 3. 发送同步消息(普通优先级) normalHandler.post(() -> { Log.d("SyncTest", "常规任务执行"); }); // 4. 执行高优先级任务后移除屏障 asyncHandler.postDelayed(() -> { queue.removeSyncBarrier(barrierToken); Log.d("SyncTest", "同步屏障已移除"); }, 100);