文章目录
最近碰到ANR的问题,需要分析定位。可是ANR的问题是真的难受,有时候即使手握着trace.txt日志也无法看到端倪,因为一般ANR问题出现都伴随着高CPU、高内存占用,确实难以定位。
花了一些时间学习Android ANR 问题的引发和系统如何检测ANR问题,以下做个记录,方便以后追溯,好记性不如烂笔头。
1. ANR问题简介
ANR(App Not Respond)表示程序在一定时间内没有反应。
根本原因就是ui线程长时间无法处理消息或者处理消息时间过长。
2. 常见的ANR问题
主要分成三类
- InputEvent输入事件: 5s
- Service服务: 前台服务20s,后台服务200s
- Broadcast: 前台队列10s,后台队列20s
3. Service 如何检测 ANR 问题
Service的监测ANR是利用定时消息处理的。
在学习Service的启动流程之后你应该知道,AMS是作为一个分发任务的角色,真正处理启动Service的是ActiveServices。
ActiveServices有一个scheduleServiceTimeoutLocked方法,当创建service时候会被调用。
// ActiveService
void scheduleServiceTimeoutLocked(ProcessRecord proc) {
if (proc.executingServices.size() == 0 || proc.thread == null) {
return;
}
Message msg = mAm.mHandler.obtainMessage(
ActivityManagerService.SERVICE_TIMEOUT_MSG);
msg.obj = proc;
mAm.mHandler.sendMessageDelayed(msg,
proc.execServicesFg ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);
}
在启动了service以后会不停发一个延迟消息ActivityManagerService.SERVICE_TIMEOUT_MSG
上述代码中,有两个变量:
mAm就是AcivityManagerService,他是由SystemServer启动,运行在独立线程。
mHanlder就是AcivityManagerService的Hanlder,并不运行在AMS的线程中,而是运行在
AMS启动的HandlerThread(名字是MainHandler)。
根据proc.execServiceFg 判断是前台服务还是后台服务,决定延迟时间
// ActiveServices
// 前台服务的ANR时间
static final int SERVICE_TIMEOUT = 20*1000;
// 后台服务的ANR时间
static final int SERVICE_BACKGROUND_TIMEOUT = SERVICE_TIMEOUT * 10;
我们看AMS如何处理ActivityManagerService.SERVICE_TIMEOUT_MSG
消息
// ActiveServices
void serviceTimeout(ProcessRecord proc) {
String anrMessage = null;
synchronized(mAm) {
// 当前进程没有运行需要检测的services
if (proc.executingServices.size() == 0 || proc.thread == null) {
return;
}
final long now = SystemClock.uptimeMillis();
final long maxTime = now -
(proc.execServicesFg ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);
ServiceRecord timeout = null;
long nextTime = 0;
for (int i=proc.executingServices.size()-1; i>=0; i--) {
ServiceRecord sr = proc.executingServices.valueAt(i);
// 遍历service并计算service是否超过anr时间
if (sr.executingStart < maxTime) {
// 找到了一个anr的service
timeout = sr;
break;
}
if (sr.executingStart > nextTime) {
nextTime = sr.executingStart;
}
}
if (timeout != null && mAm.mLruProcesses.contains(proc