3.1 ANR 原理
ANR(Application Not Responding)的监测原理本质上是消息机制,设定一个delay消息,超时未被移除则触发ANR。具体逻辑处理都在system server端,包括发送超时消息,移除超时消息,处理超时消息以及ANR弹框展示等;对于app而言,触发ANR的条件是主线程阻塞。
分类
很多情况下对应类型的ANR与正在执行的组件(ContentProvider、Broadcast、Service)本身没有关系。
- Service ANR:前台20s,后台200s;startForeground超时10s
- Broadcast ANR:前台10s,后台60s
- Input ANR:按键或触摸事件在5s内无响应
- ContentProvider ANR:10s,少见
常见原因
- 主线程binder耗时
- 主线程i/o耗时
- 死锁
- 主线程等锁耗时
- activity resume慢/焦点窗口切换慢(针对Input anr)
- 主线程有太多消息要执行,导致对应组件消息执行delay。
- 主线程前面的消息执行耗时,导致后面的消息delay。
- 跟手机当时状态有关,内存/cpu等
- startForeground未在Service的onStartCommand中执行
如何分析
步骤
确定anr的基本信息 先搜索am_anr 确定anr发生时间点、类型、超时、进程等。
查看traces文件 查看/data/anr下的对应1中的进程和时间点的traces文件, 如果很明显主线程处于Blocked状态切utm/stm时间较长则可直接通过堆栈确认原因。
查看对应时间段的event log/ main log等 但是很多情况下并不能恰如其分抓到anr发生时的堆栈,所以还得通过其他log来确认原因。 搜索 “ANR in”往前时间点搜索pid 相关信息(在03-04 13:45:03.781 往前推8s时间内)
关键字眼
- Slow Operation /am_lifecycle_sample
- Slow Looper main
- Slow Binder /binder_sample
- dvm_lock_sample
常见实例
i/o耗时
io耗时导致110消息执行时间过长,导致Receiver消息迟迟得不到执行
Service的startFrouground使用不当
startForeground一般需要在onStartCommand方法里调用,而不是在Service的onCreate中。因为如果你onStartCommand的返回值是START_NOT_STICKY,进程被杀后重启不会回调onCreate方法,但是还是会有还会走ANR的逻辑。
进程crash后不会立刻拉起Service,而是通过计算delay去启动,此处delay时间是6268ms,log中正好6s后才开始通过MxActivateService拉起短信进程
startForeground ANR的超时时间是10s,计算时间起始点是system server端post app端去执行Service的onStartCommand时。如果在onStartCommand开始执行后10s内未调用startForeground方法就会发生ANR。
主线程等锁耗时
发生ANR的8s内,home主线程有等锁11s,导致input事件未被及时处理。
binder 耗时
app查询另一个进程的content provider,binder耗时5s多
对端进程pid为7467,isLocked持锁5s多,导致isOpen等锁
activity resume慢/焦点窗口切换慢
09-18 18:22:46.380 system server端已经post app执行
TransparentAliuserGuideActivity启动流程(onCreate、onStart、onResume)
在2021-09-18 18:22:52打印anr traces时(6s后),activity还在执行onCreate,app主线程resume慢
复杂场景
app主线程等锁0x083a79bd处于Blocked状态
该锁0x083a79bd被Ad-SingleThread线程持有,且线程当前正在等待binder对端响应
查看traces中system server进程,搜索getContentProvider找到对应的binder线程
查找AMS中上述堆栈对应代码 这段代码是在等待对应provider发布完成,如果provider所在进程未启动,则先拉起该进程;超时时间为20s。
结合对应时间点的对端(Content Provider所在)进程的event log
综上:content provider拉起进程时,进程刚拉起尚未publish provider就被杀死,所以getContentProviderImpl 等待耗时,导致bindet耗时,app端子线程持锁耗时,主线程等锁耗时,从而发生ANR。
ANR 原理
Service ANR
system server在service的相关流程执行前会发送delay消息到main handler中,如果app段在对应delay消息内未通知system server段移除消息,则执行超时消息,触发anr。 SERVICE_TIMEOUT_MSG delay时间为前台service 20s,后台service 200s
service的anr主要总结为如下表格中的三个流程:
原因/流程 | ActiveServices | Service | 超时时间 |
create | realStartServiceLocked | onCreate | 20s/200s |
start | sendServiceArgs | onStartCommand | 20s/200s |
foreground | sendServiceArgs | onStartCommand | 10s |
创建service
发送delay消息
在ActiveService的realStartServiceLocked方法中发送delay消息,前台20s,后台200s;再去通知app端执行create service相关流程。
移除delay消息
app端收到system server的binder回调scheduleCreateService后,发送消息到主线程的Handler,等到、Service的onCreate方法执行完后,通过binder serviceDoneExecuting告知system server执行完毕,移除MainHandler中相关的delay消息。
启动service
发送delay消息
system server在start service前post一个SERVICE_TIMEOUT_MSG delay消息到MainHandler, 对应system server的是sendServiceArgsLocked方法,对应app端的是onStartCommand方法。
startService时,若service存在,即已进被创建,则直接调用sendServiceArgsLocked方法,否则等service create之后再调用该方法。
移除delay消息
app端收到system server的binder回调scheduleServiceArgs后,发送消息到主线程的Handler,等到相关消息被执行,Service的onStartCommand方法执行完后,通过binder serviceDoneExecuting告知system server执行完毕,移除MainHandler中相关的delay消息。
前台service调用startForeground超时
发送delay消息
调用startForegroundService 启动前台Service ,需要在onStartCommand调用前post 一个 delay 10s的SERVICE_FOREGROUND_TIMEOUT_MSG消息到MainHandler中
如果客户端10s内未及时调用startForeground,则会 触发ANR
移除delay消息
客户端调用startForeground
Service.startForeground
→ AMS.setServiceForeground
→ ActiveServices.setServiceForegroundLocked
→ setServiceForegroundInnerLocked
→ MainHandler.removeMessages
执行超时消息,触发ANR
- 若binder耗时(比较少见);
- app主线程执行一些消息耗时导致service相关的消息得不到执行 ;
- 或Service本身的周期方法onCreate,onStartCommand等执行一些耗时操作;
导致20s(前台service)或200s(后台service)或10s(startForeground)内app端未及时binder回调serviceDoneExecuting告知AMS,MainHandler则会执行到delay的消息,并调用appNotResponding方法,造成ANR现象。
MainHandler
→ ActiveServices.serviceTimeout(serviceForegroundTimeout)
→ ProcessRecord.appNotResponding
Broacast超时
发送delay消息 前台广播10s,后台广播60s
BroadcastHandler
→ BroadcastQueue . processNextBroadcast
→ processNextBroadcastLocked
→ setBroadcastTimeoutLocked
→BroadcastHandler.sendMessageAtTime
移除delay消息
Binder
→ AMS.finishReceiver
→ BroadcastQueue.processNextBroadcastLocked
→ cancelBroadcastTimeoutLocked
→ BroadcastHandler.removeMessages
执行delay消息
BroadcastHandler
→ BroadcastQueue.broadcastTimeoutLocked
→ AppNotResponding.run
→ ProcessRecord.appNotResponding
ContentProvider超时
发送delay消息
Binder
→ AMS.attachApplication
→ attachApplicationLocked
→ MainHandler.sendMessageDelayed
移除delay消息
Binder→ AMS.publishContentProviders → MainHandler.removeMessages
执行delay消息 直接杀死进程以及清理相关信息,不会弹ANR弹窗
MainHandler
→ processContentProviderPublishTimedOutLocked
→ cleanupAppInLaunchingProvidersLocked
→ ProcessList.removeProcessLocked
Input超时
上报堆栈如下: