先把结论放在前面:线上卡顿监控要在"主线程卡住的那一刻"把现场抓下来,而不是事后统计。因为卡顿的现场(主线程栈、页面上下文、Binder 阻塞数)会随时间消失——等用户反馈"刚才卡了一下"再去查,往往什么都查不到。Looper.Printer(或等价的主线程空闲检测)与Choreographer帧回调,是两把最基础也最有效的抓手。
面试里这题属于"可观测性"专题。答得浅的是"用Choreographer算帧率";答得深的是能讲清Looper.setMessageLogging只能测消息级耗时、无法覆盖消息内部的长耗时与锁等待、IdleHandler心跳检测的原理与误报边界、以及如何设计一套"卡顿采样 + 现场快照 + 聚合告警"的完整方案。
Looper Printer 能测到什么
Looper.setMessageLogging的机制是:在主线程MessageQueue取消息前后各打一个时间戳,差值就是这条消息的执行耗时。它实现简单、开销极小,是很多团队的第一选择。
/** 方案一:Looper.Printer 测消息级耗时 */ class LooperPrinter(private val delegate: Looper.Printer?) { private var start = 0L private