Android系统是如何检测到ANR- 应用无响应的?

起言

最近debug应用时,发现只要看debug的变量内容太久了,app也会爆出anr问题,遂想对ANR做一个系统的整理。

正文

导致ANR

导致ANR的直观原因在Android官网已经说的很清楚了

---- 引用自ANR | App quality | Android Developers

出现以下任何情况时,系统都会针对您的应用触发 ANR:

  • 输入调度超时:如果您的应用在 5 秒内未响应输入事件(例如按键或屏幕触摸)。
  • 执行服务 :如果应用声明的服务无法在几秒内完成 Service.onCreate()Service.onStartCommand()/Service.onBind() 执行。
  • 未调用 Service.startForeground() :如果您的应用使用 Context.startForegroundService() 在前台启动新服务,但该服务在 5 秒内未调用 startForeground()
  • intent 广播 :如果 BroadcastReceiver 在设定的一段时间内没有执行完毕。如果应用有任何前台 activity,此超时期限为 5 秒。
  • JobScheduler 互动 :如果 JobService 未在几秒钟内从 JobService.onStartJob()JobService.onStopJob() 返回,或者如果用户发起的作业启动,而您的应用在调用 JobService.onStartJob() 后的几秒内未调用 JobService.setNotification()。对于以 Android 13 及更低版本为目标平台的应用,ANR 会保持静默状态,且不会报告给应用。对于以 Android 14 及更高版本为目标平台的应用,ANR 会保持活动状态,并会报告给应用。

甚至连如何查找ANR的考量因素Google大大都为我们考虑到了,干,此文结束

治理ANR

诊断 ANR 时需要考虑以下几种常见模式:

  • 应用在主线程上非常缓慢地执行涉及 I/O 的操作。
  • 应用在主线程上进行长时间的计算。
  • 主线程在对另一个进程进行同步 binder 调用,而后者需要很长时间才能返回。
  • 主线程处于阻塞状态,为发生在另一个线程上的长操作等待同步的块。
  • 主线程在进程中或通过 binder 调用与另一个线程之间发生死锁。主线程不只是在等待长操作执行完毕,而且处于死锁状态。如需了解详情,请参阅维基百科上的死锁

但是,比起这些,我更好奇的是Android官方是如何检测到ANR的发生的?要知道,ANR发生如上文所述涉及到多个组件,Google有一套通用的实现吗

监测ANR

答案就是Handler,实际上Android系统内部很多地方都用到了handler,要写出优秀的设计,很多时候都可以考虑使用handler。

推荐阅读:Android ANR?谁控制了触发时间?

可以发现,通用套路就是向Handler发送了一个延迟消息,内容为执行ANR逻辑,如果事件被消费了,就把这个延迟消息取消掉,如果没有被消费,则消息不被取消,执行ANR产生逻辑。

示例代码如下:

java 复制代码
private Runnable anrRunnable = new Runnable() {
     @Override
     public void run() {
         Log.e(TAG, "ANR detected!");
     }
};

public void startDoSomething() {
    // 启动服务、、处理事件、、
    handler.postDelayed(anrRunnable, ANR_TIMEOUT);
}

public void finishDoSomething() {
    // 做完了,可以取消了
    handler.removeCallbacks(anrRunnable);
    // handler.removeMessages(TAG);
}

文中没有的内容补充:

对于输入事件,第一个输入事件产生后,又产生了第二个输入事件,此时若过0.5s了第一个输入事件还没有处理完毕,则设置5s的计时器,5s后第一个输入还未处理完毕,则产生ANR。

ANR后发生了什么
  1. 发送 SIGQUIT 信号:system_server 进程检测到ANR后,向应用程序进程发送 SIGQUIT 信号。
  2. 接收 SIGQUIT 信号:应用程序进程中的 libart.so 处理该信号。
  3. 调用 dump 方法:libart.so 调用Java虚拟机的 dump 方法生成线程转储。
  4. 生成 traces.txt 文件:线程转储信息被写入 traces.txt 文件,用于分析和调试ANR问题。

结语

Android官方的很多设计都值得学习,不知道大家了解到安卓检测ANR的方式后有咩有感到惊讶呢。

相关推荐
风象南5 分钟前
SpringBoot 控制器的动态注册与卸载
java·spring boot·后端
醇醛酸醚酮酯23 分钟前
Qt项目锻炼——TODO清单(二)
开发语言·数据库·qt
jioulongzi28 分钟前
记录一次莫名奇妙的跨域502(badgateway)错误
开发语言·python
我是一只代码狗31 分钟前
springboot中使用线程池
java·spring boot·后端
hello早上好44 分钟前
JDK 代理原理
java·spring boot·spring
PanZonghui1 小时前
Centos项目部署之Java安装与配置
java·linux
向阳@向远方1 小时前
第二章 简单程序设计
开发语言·c++·算法
沉着的码农1 小时前
【设计模式】基于责任链模式的参数校验
java·spring boot·分布式
Mr_Xuhhh2 小时前
信号与槽的总结
java·开发语言·数据库·c++·qt·系统架构
纳兰青华2 小时前
bean注入的过程中,Property of ‘java.util.ArrayList‘ type cannot be injected by ‘List‘
java·开发语言·spring·list