安卓入门五十四 ANR

3.2 ANR 触发原理

在Android 系统中,触发ANR的事件场景通常有四种:

Service Timeout:Service在特定的时间内无法处理完成,前台服务20秒内,后台服务在200秒内没有执行完毕。

BroadcastQueue Timeout:BroadcastReceiver在特定时间内无法处理完成,在执行前台广播(BroadcastReceiver)的onReceive()函数时10秒没有处理完成,后台为60秒

ContentProvider Timeout:内容提供者执行超时,ContentProvider的publish在10s内没进行完。

inputDispatching Timeout:按键或触摸事件在特定时间内无响应。5秒内无法响应屏幕触摸事件或键盘输入事件。

场景       事件       时间限制

Service     ActiveServices#SERVICE_TIMEOUT 20s

Service     ActiveServices#SERVICE_BACKGROUND_TIMEOUT        200s

BroadcastQueue      ActivityManagerService#BROADCAST_FG_TIMEOUT     10s

BroadcastQueue      ActivityManagerService#BROADCAST_BG_TIMEOUT    60s

ContentProvider       ActivityManagerService#CONTENT_PROVIDER_PUBLISH_TIMEOUT    10s

inputDispatching      ActivityTaskManagerService#KEY_DISPATCHING_TIMEOUT  5s

现在先查看 scheduleServiceTimeoutLocked 方法的具体实现:

在这个方法中, 会 通过 mAm (也就是ActivityManagerService )的 mHandler 发送一个延迟消息。这个消息的基本参数值信息是:

mHandler :通过子线程的 Looper 创建的一个 Handler,所以这个消息是放在子线程的消息队列中,不影响主线程。

what :ActivityManagerService.SERVICE_TIMEOUT_MSG(值12)。

delayMillis :如果是前台服务就是 SERVICE_TIMEOUT(值20*1000豪秒),否则是SERVICE_BACKGROUND_TIMEOUT(10 * SERVICE_TIMEOUT豪秒)。

如果 Service 成功启动,也就是在规定的 SERVICE_TIMEOUT 或 SERVICE_BACKGROUND_TIMEOUT 时间范围内执行完成该事件,就会移除掉这个延迟消息:

否则就会在ActivityManagerService中触发这个延迟消息:

再看 ActiveServices 的 serviceTimeout 方法:

再看 proc(也就是 ProcessRecord) 的 appNotResponding 方法:

这个方法做的主要事情是:

收集应用程序最近使用的的进程 pid,只收集前五个进程 pid;

在控制台打印ANR相关的日志,日志包含:当前进程名字,当前组件信息,当前进程id,前五个进程的相关信息,cpu信息等;

向进程发送放弃信号,也就是退出进程信号,这里会打印日志;

通过AtivityManagerService#addErrorToDropBox,将这次"ANR"的进程相关信息,组件信息,cpu信息和traces文件保存到dropbox,即data/system/dropbox目录中;

交给 ActivityManagerService 处理,再交给AppErrors 处理,然后弹出程序无响应对话框。

总结

综上,创建 Service 触发 ANR的原理就是:在创建 Service 的时候,也就是调用 Service 的 onCreate 方法前会发送一个延时消息(如果是前台服务,延时时间就是 SERVICE_TIMEOUT(值20*1000豪秒),否则延时时间是SERVICE_BACKGROUND_TIMEOUT(10 * SERVICE_TIMEOUT豪秒))。),如果onCreate 方法在20 /200秒内执行完成,就会移除这个延时消息,不会触发 ANR;否则就会执行这个延时消息,并触发 ANR。

Android ANR 触发原理_anr 应用被杀-CSDN博客

3.3 ANR分析方法

尽量避免在主线程(UI线程)中作耗时操作。

2.1 ANR重现

这里使用的是号称Google亲儿子的Google Pixel xl(Android 8.0系统)做的测试,生成一个按钮跳转到ANRTestActivity,在后者的onCreate()中主线程休眠20秒:

在进入ANRTestActivity后黑屏一段时间,大概有七八秒,终于弹出了ANR异常。

2.2 ANR分析办法一:Log

刚才产生ANR后,看下Log:

可以看到logcat清晰地记录了ANR发生的时间,以及线程的tid和一句话概括原因:WaitingInMainSignalCatcherLoop,大概意思为主线程等待异常。
最后一句The application may be doing too much work on its main thread.告知可能在主线程做了太多的工作。

2.3 ANR分析办法二:traces.txt

刚才的log有第二句Wrote stack traces to '/data/anr/traces.txt',说明ANR异常已经输出到traces.txt文件,使用adb命令把这个文件从手机里导出来:

cd到adb.exe所在的目录,也就是Android SDK的platform-tools目录,例如:

此外,除了Windows的cmd以外,还可以使用AndroidStudio的Terminal来输入adb命令。

到指定目录后执行以下adb命令导出traces.txt文件:

traces.txt默认会被导出到Android SDK的\platform-tools目录。一般来说traces.txt文件记录的东西会比较多,分析的时候需要有针对性地去找相关记录。



在文件中使用 ctrl + F 查找包名可以快速定位相关代码。
通过上方log可以看出相关问题:

  • 进程id和包名:pid 23346 com.sky.myjavatest
  • 造成ANR的原因:Sleeping
  • 造成ANR的具体行数:ANRTestActivity.java:20类的第20行

特别注意:产生新的ANR,原来的 traces.txt 文件会被覆盖。

2.4 ANR分析办法三:Java线程调用分析

通过JDK提供的命令可以帮助分析和调试Java应用,命令为:

其中pid可以通过jps命令获得,jps命令会列出当前系统中运行的所有Java虚拟机进程,比如

2.5 ANR分析办法四:DDMS分析ANR问题

使用DDMS——Update Threads工具

阅读Update Threads的输出

三、造成ANR的原因及解决办法

上面例子只是由于简单的主线程耗时操作造成的ANR,造成ANR的原因还有很多:

  • 主线程阻塞或主线程数据读取

解决办法:避免死锁的出现,使用子线程来处理耗时操作或阻塞任务。尽量避免在主线程query provider、不要滥用SharePreferenceS

  • CPU满负荷,I/O阻塞

解决办法:文件读写或数据库操作放在子线程异步操作。

  • 内存不足

解决办法:AndroidManifest.xml文件<applicatiion>中可以设置 android:largeHeap="true",以此增大App使用内存。不过不建议使用此法,从根本上防止内存泄漏,优化内存使用才是正道。

  • 各大组件ANR

各大组件生命周期中也应避免耗时操作,注意BroadcastReciever的onRecieve()、后台Service和ContentProvider也不要执行太长时间的任务。

四、ANR源码分析

4.1 Service造成的Service Timeout

Service Timeout是位于"ActivityManager"线程中的AMS.MainHandler收到SERVICE_TIMEOUT_MSG消息时触发。

4.1.1 发送延时消息

Service进程attach到system_server进程的过程中会调用realStartServiceLocked,紧接着mAm.mHandler.sendMessageAtTime()来发送一个延时消息,延时的时常是定义好的,如前台Service的20秒。ActivityManager线程中的AMS.MainHandler收到SERVICE_TIMEOUT_MSG消息时会触发。

AS.realStartServiceLocked


ActiveServices.java

AS.bumpServiceExecutingLocked

4.1.2 进入目标进程的主线程创建Service

经过Binder等层层调用进入目标进程的主线程

handleCreateService(CreateServiceData data)。

ActivityThread.java

这个方法中会创建目标服务对象,以及回调常用的Service的onCreate()方法,紧接着通过serviceDoneExecuting()回到system_server执行取消AMS.MainHandler的延时消息。

4.1.3 回到system_server执行取消AMS.MainHandler的延时消息

AS.serviceDoneExecutingLocked

此方法中Service逻辑处理完成则移除之前延时的消息SERVICE_TIMEOUT_MSG。如果没有执行完毕不调用这个方法,则超时后会发出SERVICE_TIMEOUT_MSG来告知ANR发生。

4.2 BroadcastReceiver造成的BroadcastQueue Timeout

BroadcastReceiver Timeout是位于"ActivityManager"线程中的BroadcastQueue.BroadcastHandler收到BROADCAST_TIMEOUT_MSG消息时触发。

4.2.1 处理广播函数 processNextBroadcast() 中 broadcastTimeoutLocked(false) 发送延时消息

广播处理顺序为先处理并行广播,再处理当前有序广播。

上文的step 1. broadcastTimeoutLocked(false)函数:记录时间信息并调用函数设置发送延时消息

上文的step 2.setBroadcastTimeoutLocked函数: 设置广播超时具体操作,同样是发送延时消息

4.2.2 setBroadcastTimeoutLocked(long timeoutTime)函数的参数timeoutTime是当前时间加上设定好的超时时间。

也就是上文的

mTimeoutPeriod 也就是前台队列的10s和后台队列的60s。

4.2.3 在processNextBroadcast()过程,执行完performReceiveLocked后调用cancelBroadcastTimeoutLocked

cancelBroadcastTimeoutLocked :处理广播消息函数 processNextBroadcast() 中 performReceiveLocked() 处理广播消息完毕则调用 cancelBroadcastTimeoutLocked() 取消超时消息。

4.3 ContentProvider的ContentProvider Timeout

ContentProvider Timeout是位于”ActivityManager”线程中的AMS.MainHandler收到CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG消息时触发。
参考理解Android ANR的触发原理第四节

五、Android ANR的信息收集

无论是四大组件或者进程等只要发生ANR,最终都会调用AMS.appNotResponding()方法。
参考:理解Android ANR的信息收集过程

Android ANR:原理分析及解决办法 - 简书 (jianshu.com)