定位及修复在定位到原因之后,接下来就是找到问题并解决问题,具体什么样的改动会引起这里问题了,通过分析我们知道既然是 Barrier 消息同步的问题,那么我们可以在设置 barrier 和移除 barrier 的过程加入监控,判断哪里设置了 barrier 消息,但是没有同步移除 。通过 Java hook 代理了 MessageQueue 的 postSyncBarrier 和 removeSyncBarrier 接口,进行 Barrier 消息同步监测,遗憾的是线下并没有复现 。
因此只能再次回到代码层面,对相关改动进行分析,最终在一笔需求提交中发现了线索 。
逻辑调整前: 先移除将要强制调度的并设置了异步属性的消息,再强制调度该消息,以保证该消息不受 barrier 消息之前的消息 block,进而提高响应能力 。
if (hasMsg) { ...... handler.removeCallbacks(message.getCallback()); //先移除 handler.dispatchMessage(cloneMsg); //再强制调度该消息 ......}逻辑调整后: 先强制调度该消息,然后再将该消息从队列中移除 。
...... handler.dispatchMessage(newMessage); //先强制调度 handler.removeCallbacks(message.getCallback()); //从队列中移除消息 ...... }但是时序调整后存在一定隐患,即在强制调用 DoFrame 消息期间,业务可能会再次触发 UI 刷新逻辑,产生 barrier 消息并发出 vsync 请求,如果系统及时响应 vsync,并产生 DoFrame 消息,那么调用 removeCallbacks 接口会一次性清除消息队列中所有的 DoFrame 消息,即:移除了消息队列之前的 DoFrame 消息和下次待调度的 DoFrame 消息,但是与下次 DoFrame 消息同步的 barrier 消息并没有被移除 。
那么为什么会移除多个消息呢?这就要从handler.removeCallbacks 的实现说起了 。

文章插图
进一步查看
messageQueue.removeMessages 接口实现,发现该接口会遍历消息队列中符合当前 runnable 以及 object 的消息,但是上面传递的 Object 对象是 null,因此就相当于移除了当前 Handler 对象下面所有相同 runnable 对象的消息!

文章插图
因为强制刷新和时序调整的问题,导致了消息队列中同时存在 2 个 UI doFrame 消息,并在强制执行之后被同时移除,从而导致一个无人认领的 barrier 消息一直停留在消息队列 !
其它场景:此外,除了上面遇到的场景会导致这类问题之外,还有一种场景也可能会导致这类问题,即:UI 异步刷新,尽管 Android 系统禁止异步刷新,并利用 checkThread 机制对 UI 刷新进行线程检查,但是百密一疏,如果开启硬件加速,在 AndroidO 及之后的版本会间接调用 onDescendantInvalidated 触发 UI 刷新,该逻辑躲过了系统 checkThread 检查,将会造成线程并发隐患 。如下图,如果并发执行则会导致前一个线程的 mTraversalBarrier 被覆盖,从而导致 vsync 消息与 barrier 出现同步问题 。

文章插图
查看 Android Q 源码,看到 onDescendantInvalidated 内部加上了 checkThread,但被注释掉了!解释如下:修复摄像头后重新启用或者通过 targetSdk 检查?好吧,或许是忘记这个 TODO 了 。

文章插图
总结:至此,我们完成了该类问题的分析和最终定位,综合来看该类问题因 Trace 场景(NativePollOnce)和问题本身的高度隐蔽性,给排查和定位带来了极大挑战,如果单纯依靠系统提供的日志,是很难发现 MessageQueue.next()内部发生了异常 。这里我们通过 Raster 监控工具,还原了问题现场,并提供了重要线索 。现在总结来看,该类问题其实具有很明显的特征,表现在以下几个方面:
- 问题场景 ANR Trace 集中聚合在 NativePollOnce,此过程 Wall duration 持续很长,并且屏蔽了后续所有正常消息调度,看起来像主线被冻结一样 。
- 通过 Raster 监控工具可以清晰的看到,消息队列中如果第一个待消息 target 为 null,即为 barrier 消息,可以通过后续消息 block 时长评估影响程度 。
- 出现该类问题时,因为正常消息无法被调度,如 Service,Receiver 消息,将会导致应用连续发生 ANR,直到用户主动 Kill 该进程 。
推荐阅读
- 对于初入头条的创作者,你有哪些建议?
- 渐变工具调整法 今日带你认识PS颜色调整
- 何为幂等?如何设计?
- 对于今日头条,百度百家,大鱼号,企鹅号这四个自媒体你们是怎么看待的?
- Mac上我常用的几个软件
- 中青宝再度涨停收盘?中青宝今日股价
- 头条号怎么开通收益?信用分扣了40分怎么办?方法都在这里
- 在明朝,皇帝的第二代被称为 被康熙皇帝赞许为今日清宫第一的清代官员是
- 今日春分春分养生 春分养生的秘诀有哪些
- 新手做西瓜头条,分享5个无版权,高清素材图库,赶紧收藏
