插入同步屏障,但messagequeue没异步消息
依赖同步消息的逻辑会卡住
「插入了同步屏障但队列中没有异步消息」的实际表现:不会“彻底卡住”,而是等待异步消息到来或超时唤醒
源码分析(基于 AOSP)
在 MessageQueue.next() 中:
Message next() {
for (;;) {
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
// 同步屏障:target == null
if (msg != null && msg.target == null) {
// 遍历找异步消息
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
if (msg != null) {
// 找到了异步消息,取出来返回
...
return msg;
} else {
// ❗ 没有异步消息 → 计算下次超时时间,继续 nativePollOnce 阻塞等待
nextPollTimeoutMillis = -1; // 无限等待
continue;
}
}
...
}
}
}
正确结论如下:
情况 | 是否“卡死”? | 解释 |
---|---|---|
同步屏障 + 有异步消息(如 VSync) | ❌ | 正常工作,执行异步消息 |
同步屏障 + 没有异步消息 | ✅ 可能卡住 | 会进入 nativePollOnce 无限等待状态,直到异步消息或 wake() 触发唤醒 |
同步屏障移除前无异步消息插入 | ✅ 卡住 | UI 不更新,逻辑挂起,但不是线程挂死,可以被 wake 唤醒 |
更严谨地总结
• 并不是 MessageQueue.next() 不再返回;
• 而是由于找不到异步消息,它 会进入无限等待状态(nextPollTimeoutMillis = -1);
• 除非后续有异步消息加入(比如 VSync 回调),或手动调用 MessageQueue.wake();
• 否则主线程不会主动恢复处理消息,表现为“卡死”。
应该更精确地说:
插入同步屏障后,如果没有异步消息,会导致主线程进入无限等待状态,不会继续处理后续同步消息,表现为依赖同步消息的逻辑卡住。
不影响UI更新
但是UI 还是会更新的,前提是:
异步消息(如 VSync 回调)仍然在运行,Choreographer 的机制没有出问题。
我们回到你最初的条件:
插入了同步屏障,但 MessageQueue 中没有异步消息 —— UI 明明还能更新。
那就必须具体一点:
关键区别:你是在哪一刻插入同步屏障?
如果只是你手动插入同步屏障(比如调试用)
• 而此时 Choreographer 已经注册了 VSync 监听,
• 那么下一帧的 VSync 到来后,FrameDisplayEventReceiver 会以异步消息形式进入 MessageQueue,
• ➜ 所以 UI 依然能更新!
换句话说:
即使你插入了同步屏障,只要 还有异步消息能进队列(比如系统自动注入的 VSync),
MessageQueue.next() 依然会返回这些异步消息,执行 doFrame(),UI 照常更新。
但真正的问题是——如果异步消息“完全没有”会发生什么?
也就是说:插入屏障,且未来没有任何异步消息(不触发 VSync、不手动插入异步消息)
这时:
• MessageQueue.next() 会在 nativePollOnce(-1) 里阻塞;
• 找不到任何可执行消息;
• ➜ UI 更新不会发生,应用逻辑暂停运行,但不是线程挂死,只是等待。
所以你观察到 UI 能更新,是因为系统帮你安排了异步消息(比如 VSync)
你说的**“UI 明明更新了”是对的**,因为系统的 Choreographer 机制会安排异步的 VSync 回调消息,这些能穿过同步屏障,所以:
插入同步屏障 ≠ UI 停止更新
没有异步消息 + 插入同步屏障 ⇒ UI 不再更新(除非 wake)
最终精确总结
情况 | 是否 UI 更新 | 原因 |
---|---|---|
插入同步屏障 + 有异步消息(VSync) | ✅ 会更新 | 异步消息穿越屏障被执行,触发 doFrame() |
插入同步屏障 + 无异步消息(无 VSync、无 wake) | ❌ 不更新 | 所有同步消息被挡住,异步消息又没来,线程卡在 pollOnce |
插入屏障 + 马上手动调用 postAsynchronousMessage() | ✅ 会更新 | 能穿过屏障,唤醒线程 |