例子
同步屏障没用好会导致很严重的问题,如果某个子线程主动通过Choreographer来进行更新UI,但是由于某种原因导致放置的同步屏障未能remove掉,就会导致后续的所有同步任务都无法执行。之前就接触过一例问题,就是由于系统主线程的同步屏障未能正常被remove,从而导致了所有的系统功能(依赖于handler的同步任务)都无法正常调用,排查了好久才定位到是由于同步屏障的原因。 模拟下这个问题
模拟主线程插入同步屏障 + 没有移除,导致 Handler.post() 这样的同步任务永远不执行,但 UI invalidate() 仍然生效,绘制正常。
示例代码(重点放在“部分功能失效”而非“绘制失效”)
public class SyncBarrierDemoActivity extends AppCompatActivity {
private int barrierToken = -1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
TextView tv = new TextView(this);
tv.setText("点击我插入同步屏障,然后再试试按钮");
tv.setTextSize(20);
setContentView(tv);
tv.setOnClickListener(v -> {
Log.d("SyncTest", "点击事件触发");
insertSyncBarrier(); // ❗插入但不移除
v.setText("已插入同步屏障");
// 异步注册一次 invalidate
v.postDelayed(() -> {
Log.d("SyncTest", "尝试更新文本(postDelayed)");
v.setText("这行永远不会出现!");
}, 3000);
});
}
private void insertSyncBarrier() {
try {
MessageQueue queue = Looper.getMainLooper().getQueue();
Method postSyncBarrier = MessageQueue.class.getDeclaredMethod("postSyncBarrier");
postSyncBarrier.setAccessible(true);
barrierToken = (int) postSyncBarrier.invoke(queue);
Log.d("SyncTest", "同步屏障插入成功 barrierToken=" + barrierToken);
} catch (Exception e) {
e.printStackTrace();
}
}
}
模拟效果:
项目 | 是否执行 | 原因 |
---|---|---|
setOnClickListener 响应 | ✅ 会 | 是 UI 主线程执行的回调,不依赖 Handler |
invalidate()、重绘 View | ✅ 会 | 最终通过 Choreographer 异步驱动 |
Handler.postDelayed() | ❌ 不会 | 默认是同步消息,被同步屏障阻塞了! |
Toast.show() / Dialog | ❌ 不会 | 系统内部使用 Handler,同样阻塞 |
如果你要模拟“彻底死锁 UI”,需要:
你必须主动移除 Choreographer 的异步注册回调(即 doFrame() 不再注册),或在特殊时机提前插入屏障阻止它注册。
这在现实中几乎不会发生,除非你故意干坏事,或者某些框架用了危险的反射 +屏障逻辑。
最终结论
错误说法(我之前) | 正确修正 |
---|---|
插入屏障会导致 UI 卡死 | ❌ UI 绘制流程是异步驱动,不会死 |
invalidate() 会失效 | ❌ 不会失效,会照常触发重新绘制 |
所有消息都卡住了 | ❌ 只有同步消息,如 post() / postDelayed() 被卡 |