前言
关于Service的ANR场景还是比较少见的,它的ANR设计原理也是比较简单,在很多监控的方案中都可以看得到
ANR触发场景
通过查阅Android官方文档,我们知道出现以下任何情况,系统都会针对我们的应用触发ANR:
- Service Timeout:比如前台服务在20s内未执行完成,后台服务Timeout时间是前台服务的10倍,200s
- BroadcastQueue Timeout:比如前台广播在10s内未执行完成,后台60s
- ContentProvider Timeout:内容提供者,在publish过超时10s
- InputDispatching Timeout: 输入事件分发超时5s,包括按键和触摸事件
Service的工作机制
在了解Service触发ANR之前,要先了解Service的工作机制
- ContextImpl:ContextImpl是Android一个很重要的数据结构,它是Context的具体实现,Context中的大部分逻辑都是由ContextImpl来完成
- ActivityManagerService:简称AMS,继承自ActivityManagerNavite(简称AMN),AMN继承自Binder并实现了IActivityManager这个Binder接口,因此AMS也是一个Binder,它是IActivityManager的具体实现
- ActiveServices:辅助AMS进行Service管理的的类,包括Service的启动,绑定和停止等
- ServiceRecord:Service的记录类,可以简单理解为Service的一个栈
- ActivityThread:ActivityThread并不是一个Thread类,但代表了Android的主线程,其中的main方法是整个APP的入口,主要在Application进程中管理执行主线程,以及调度和执行活动和广播,和活动管理请求的其它操作
Service的ANR机制
- 埋炸弹:指的是在启动Service前,发送延时消息,如果Service在指定的是时间没有拆炸弹,则会引爆ANR
- 拆炸弹:在指定时间内,顺利运行Service服务,运行完成之后会拆掉一开始预埋下来的炸弹
- 引爆炸弹:在指定的时间未成功顺利运行Service服务,则会处理延时消息,引发ANR弹窗,引爆炸弹
Service埋定时炸弹
Service的启动过程分为两种
- 通过StartService启动Service
- 通过bindService启动Service
两种启动方式最终的调用链都来到ActiveServices.realStartServiceLocked()
private void realStartServiceLocked(ServiceRecord r, ProcessRecord app,
IApplicationThread thread, int pid, UidRecord uidRecord, boolean execInFg,
boolean enqueueOomAdj) throws RemoteException {
......
// 1.发送delay消息(SERVICE_TIMEOUT_MSG)
bumpServiceExecutingLocked(r, execInFg, "create", null /* oomAdjReason */);
try {
// 2.创建Service对象,并且调用onCreate()
thread.scheduleCreateService(r, r.serviceInfo,
mAm.compatibilityInfoForPackage(r.serviceInfo.applicationInfo),
app.mState.getReportedProcState());
} catch (DeadObjectException e) {
Slog.w(TAG, "Application dead when creating service " + r);
mAm.appDiedLocked(app, "Died when creating service");
throw e;
} finally {
......
}
......
}
private boolean bumpServiceExecutingLocked(ServiceRecord r, boolean fg, String why,
@Nullable String oomAdjReason) {
......
scheduleServiceTimeoutLocked(r.app);
......
}
void scheduleServiceTimeoutLocked(ProcessRecord proc) {
if (proc.mServices.numberOfExecutingServices() == 0 || proc.getThread() == null) {
return;
}
Message msg = mAm.mHandler.obtainMessage(
ActivityManagerService.SERVICE_TIMEOUT_MSG);
msg.obj = proc;
mAm.mHandler.sendMessageDelayed(msg, proc.mServices.shouldExecServicesFg()
? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);
}
ActiveServices.realStartServiceLocked()
做了两个事情
- 通过
mHandler
发送延时消息,在这里将会埋下炸弹 - 创建Service正式执行Service的启动逻辑,并且调用
Service
的onCreate()
从发送的延时时间可以看出,Service的炸弹是指前台服务Timeout时间为20s,后台服务Timeout时间是前台服务的10倍,200s
static final int SERVICE_TIMEOUT = 20*1000;
static final int SERVICE_BACKGROUND_TIMEOUT = SERVICE_TIMEOUT * 10;
Service拆炸弹
Service正常运行情况下,最终会执行到ActivityThread
的handleCreateService
方法
private void handleCreateService(CreateServiceData data) {
......
try {
ActivityManager.getService().serviceDoneExecuting(
data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
......
}
在ActiveServices
我们找到了这个移除SERVICE_TIMEOUT_MSG
消息的方法serviceDoneExecutingLocked
private void serviceDoneExecutingLocked(ServiceRecord r, boolean inDestroying,
boolean finishing, boolean enqueueOomAdj) {
......
if (psr.numberOfExecutingServices() == 0) {
if (DEBUG_SERVICE || DEBUG_SERVICE_EXECUTING) Slog.v(TAG_SERVICE_EXECUTING,
"No more executingServices of " + r.shortInstanceName);
mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app);
}
......
}
Service引爆炸弹
在创建Service之前如果消息没被移除,也就代表着Service没有被顺利的执行下来,就会导致Handler
去处理当前的Timeout消息
final class MainHandler extends Handler {
public MainHandler(Looper looper) {
super(looper, null , true);
}
@Override
public void handleMessage(Message msg) {
...
// 1.处理消息
case SERVICE_TIMEOUT_MSG: {
mServices.serviceTimeout((ProcessRecord) msg.obj);
}
...
}
}
void serviceTimeout(ProcessRecord proc) {
String anrMessage = null;
......
if (anrMessage != null) {
// 2.触发ANR弹窗
mAm.mAnrHelper.appNotResponding(proc, anrMessage);
}
}
appNotResponding
方法内会开始生成trace文件等信息,然后通过ActivityManagerService
的mUiHandler
发送SHOW_NOT_RESPONDING_UI_MSG
展示ANR弹窗