Android Hangs挂起测试是一类专门用于发现应用界面冻结、主线程长时间阻塞等问题的测试方法。与崩溃测试关注进程是否存活不同,Hangs测试关注的是应用在运行过程中是否出现“活着但没反应”的状态,也就是用户点击屏幕无响应、界面停止刷新、甚至最终触发系统的ANR(Application Not Responding)弹窗。这类问题对用户体验的杀伤力极大,而且往往难以在常规功能测试中复现,因此需要专门的测试策略和工具来系统性地挖掘。

什么是Hangs,它和ANR是什么关系
Hangs指的是应用的主线程在一定时间内无法处理任何消息,包括用户输入事件、界面绘制请求和生命周期回调。Android系统对主线程的响应能力有明确的时限约束,一旦超时就会触发ANR。理解这个机制是做挂起测试的基础。
常见的ANR触发条件有三种:第一种是输入事件超时,如果主线程在5秒内没有处理完一个输入事件,系统会判定应用无响应;第二种是前台Service超时,前台Service的 onCreate 或 onStartCommand 如果在20秒内没有执行完毕,同样会触发ANR;第三种是广播超时,前台广播的 BroadcastReceiver.onReceive 方法如果超过10秒没有返回,也会导致ANR。这三种情况本质上都是主线程被某段代码长期占用了。
需要注意的是,Hangs和ANR并不是等价的概念。Hangs是一个持续过程,只要主线程阻塞超过一帧的时长(比如16毫秒以上的卡顿就属于轻微Hangs),用户体验就已经受损,而ANR是系统在阻塞时间达到阈值后给出的最终判定。换句话说,一个应用可能天天都在卡顿,却很少真正弹出ANR弹窗,但从用户角度看它的体验已经是灾难性的。挂起测试的目标不只是避免ANR弹窗,而是把所有可感知的阻塞都暴露出来。
主线程阻塞的常见成因分析
做挂起测试之前,先要了解什么代码会把主线程挂起。排在第一位的是主线程做磁盘IO和网络请求,比如直接在主线程读取SharedPreferences大文件、读写数据库、执行网络同步调用。这些操作耗时不可控,一旦磁盘慢或者网络差,阻塞时间就会被无限放大。
第二类是锁竞争和死锁。典型的场景是主线程持有一个锁去等子线程的结果,而子线程又要等主线程释放另一个锁,双方互相等待造成永久挂起。还有一种隐蔽的情况是主线程调用 Future.get() 或 CountDownLatch.await() 而没有设置超时,一旦子线程任务失败或丢失,主线程就永远卡在那里。
第三类是Binder调用阻塞。跨进程调用看似是异步框架,但如果在主线程上同步调用另一个进程的服务,而对方进程繁忙或正在被调试器挂起,主线程就会一直阻塞在 Binder 事务上。此外,重量级的对象序列化、超大Bitmap的同步解码、主线程里做JSON反序列化等CPU密集操作,同样是常见的挂起源头。测试时应该针对这些场景设计专项用例,而不是只做随机的界面点击。
如何检测和定位Hangs问题
第一道防线是StrictMode严格模式。在应用的Debug版本中开启线程策略和虚拟机策略检测,可以捕获主线程上的磁盘读写和网络访问,并在日志中输出违规堆栈。开启方式如下:
if (BuildConfig.DEBUG) {
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectAll()
.penaltyLog()
.build());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectAll()
.penaltyLog()
.build());
}第二道防线是主线程消息监控。原理是利用Handler的机制,在主线程任务分发前后插桩打点,计算每个任务的执行耗时,一旦超过阈值就抓取当时的调用堆栈。开源库BlockCanary就是基于这个思路实现的,它能在卡顿发生时自动保存堆栈文件,方便事后分析。
第三道防线是系统级工具。开发者选项中的ANR痕迹可以通过 /data/anr/traces.txt 获取,里面记录了ANR发生时刻所有线程的状态和堆栈,是定位死锁的一手资料。对于线上问题,可以使用Perfetto或Systrace采集系统级trace,观察主线程在每个时间片内执行了什么函数,长任务在trace图上会表现为一条很长的主线程色块,配合CPU调用栈可以直接定位到具体代码行。如果需要持续监控线上用户的卡顿情况,还可以接入Firebase Performance或自建的APM平台,统计卡顿率和ANR率,形成从发现到修复的闭环。
构建自动化挂起测试的实践方案
自动化层面,最经典的做法是Monkey压测。通过adb命令向被测应用发送大量伪随机事件流,长时间运行后观察是否出现ANR:
adb shell monkey -p com.example.demo --throttle 300 -s 100 \
--ignore-crashes --ignore-timeouts -v -v 50000其中 --ignore-timeouts 参数让Monkey在遇到ANR时不停止,继续执行后续事件,这样可以统计整个测试周期内ANR出现的次数。测试结束后结合 bugreport 提取ANR日志,就能得到一份完整的挂起问题清单。
更有针对性的方案是编写自动化UI测试,主动制造挂起条件。比如用Espresso或UI Automator模拟弱网、低电、后台高负载等恶劣环境,同时在测试脚本中定期向主线程发送探测消息,如果探测消息在设定时间内没有被处理,就判定发生了挂起。这种主动探测的方式比等待系统弹出ANR更灵敏,能在问题恶化之前就发出告警。
最后建议把挂起测试纳入持续集成流水线。每次合码前自动跑一轮长时间的Monkey和UI遍历,把ANR数量、卡顿次数作为质量门禁指标,一旦超过阈值就阻断合码。同时在灰度阶段监控线上ANR率和主线程卡顿率,用真实用户数据反哺测试场景的补充。通过测试手段、工具监控和流程门禁三层配合,才能真正把Hangs问题拦截在用户感知之前。
Android HangsANR应用无响应修改时间:2026-09-05 19:26:47