本文源代码基于 Android 7.0。
这篇文章来分析一下 Android 中 ANR 的原理。
目录:
- ANR 简介
- Service ANR 分析
- 如何避免 ANR
1. ANR 简介
ANR (Application Not responding),是指应用程序未响应,Android 系统对于一些事件需要在一定的时间范围内完成,
如果超过预定时间能未能得到有效响应或者响应时间过长,都会造成 ANR。一般地,这时往往会弹出一个提示框,告知用户当前xxx未响应,用户可选择继续等待或者 Force Close。
ANR 场景:
- Service 超时:比如前台服务在 20s 内没有执行完成。
- BroadcastQueue 超时:比如前台广播在 10s 内没有执行完成。
- ContentProvider 超时:内容提供者在 publish 过超时 10s。
- InputDispatching 超时:输入事件分发超时 5s,包括按键和触摸事件。
分析 ANR 从埋炸弹,拆炸弹和引爆炸弹三个方面分析,本文章只分析 Service 的 ANR。
2. Service ANR 分析
对于前台服务,超时为 SERVICE_TIMEOUT = 20s;对于后台服务,则超时为 SERVICE_BACKGROUND_TIMEOUT = 200s。先从 startService() 作为入口。
- 2.1 埋炸弹
在 Service 进程 attach 到 system_server 进程的过程中会调用 realStartServiceLocked() 方法来埋下炸弹:
/base/services/core/java/com/android/server/am/ActiveServices.java
// 前台 Service 超时时间 20s
// How long we wait for a service to finish executing.
static final int SERVICE_TIMEOUT = 20*1000;
// 后台 Service 超时时间 200s
// How long we wait for a service to finish executing.
static final int SERVICE_BACKGROUND_TIMEOUT = SERVICE_TIMEOUT * 10;
private final void realStartServiceLocked(ServiceRecord r,
ProcessRecord app, boolean execInFg) throws RemoteException {
// ...
// 发送delay消息(SERVICE_TIMEOUT_MSG)
bumpServiceExecutingLocked(r, execInFg, "create");
// ...
try {
// ...
// 最终执行服务的onCreate()方法
app.thread.scheduleCreateService(r, r.serviceInfo,
mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
app.repProcState);
} catch (DeadObjectException e) {
// ...
} finally {
// ...
}
// ...
}
private final void bumpServiceExecutingLocked(ServiceRecord r, boolean fg, String why) {
long now = SystemClock.uptimeMillis();
if (r.executeNesting == 0) {
r.executeFg = fg;
ServiceState stracker = r.getTracker();
if (stracker != null) {
stracker.setExecuting(true, mAm.mProcessStats.getMemFactorLocked(), now);
}
if (r.app != null) {
r.app.executingServices.add(r);
r.app.execServicesFg |= fg;
if (r.app.executingServices.size() == 1) {
// 超时调度
scheduleServiceTimeoutLocked(r.app);
}
}
} else if (r.app != null && fg && !r.app.execServicesFg) {
r.app.execServicesFg = true;
// 超时调度
scheduleServiceTimeoutLocked(r.app);
}
r.executeFg |= fg;
r.executeNesting++;
r.executingStart = now;
}
void scheduleServiceTimeoutLocked(ProcessRecord proc) {
if (proc.executingServices.size() == 0 || proc.thread == null) {
return;
}
long now = SystemClock.uptimeMillis();
Message msg = mAm.mHandler.obtainMessage(
ActivityManagerService.SERVICE_TIMEOUT_MSG);
msg.obj = proc;
// 发送serive超时延时
mAm.mHandler.sendMessageAtTime(msg,
proc.execServicesFg ? (now+SERVICE_TIMEOUT) : (now+ SERVICE_BACKGROUND_TIMEOUT));
}
最后如果炸弹没有在指定时间内拆掉,就会调用 ActivityManagerService 的 Handler 发送超时消息。
- 2.2 拆炸弹
在 system_server 进程 ActiveServices.realStartServiceLocked() 调用的过程会埋下一颗炸弹,超时没有启动完成则会爆炸。那么什么时候会拆除这颗炸弹呢? 经过 Binder 等层层调用进入目标进程的主线程 handleCreateService(),前面文章讲了,每个进程都有它自己的主线程 ActivityThread。
/base/core/java/android/app/ActivityThread.java
// ActivityThread 内部的 Handler 类
private class H extends Handler {
// ...
case CREATE_SERVICE:
handleCreateService((CreateServiceData)msg.obj);
break;
}
private void handleCreateService(CreateServiceData data) {
LoadedApk packageInfo = getPackageInfoNoCheck(
data.info.applicationInfo, data.compatInfo);
Service service = null;
try {
// 反射出服务对象
java.lang.ClassLoader cl = packageInfo.getClassLoader();
service = (Service) cl.loadClass(data.info.name).newInstance();
} catch (Exception e) {
if (!mInstrumentation.onException(service, e)) {
throw new RuntimeException(
"Unable to instantiate service " + data.info.name
+ ": " + e.toString(), e);
}
}
try {
// 创建ContextImpl对象
ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
context.setOuterContext(service);
// 创建Application对象
Application app = packageInfo.makeApplication(false, mInstrumentation);
service.attach(context, this, data.info.name, data.token, app,
ActivityManagerNative.getDefault());
// 调用服务onCreate()方法
service.onCreate();
mServices.put(data.token, service);
try {
// 拆除炸弹
ActivityManagerNative.getDefault().serviceDoneExecuting(
data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
} catch (Exception e) {
if (!mInstrumentation.onException(service, e)) {
throw new RuntimeException(
"Unable to create service " + data.info.name
+ ": " + e.toString(), e);
}
}
}
OK,最后会回到 system_server 进程,到 ActivityManagerService 中去拆除炸弹。
/base/services/core/java/com/android/server/am/ActivityManagerService.java
public void serviceDoneExecuting(IBinder token, int type, int startId, int res) {
synchronized(this) {
if (!(token instanceof ServiceRecord)) {
Slog.e(TAG, "serviceDoneExecuting: Invalid service token=" + token);
throw new IllegalArgumentException("Invalid service token");
}
mServices.serviceDoneExecutingLocked((ServiceRecord)token, type, startId, res);
}
}
最终又绕回到 ActivityServices:
/base/services/core/java/com/android/server/am/ActiveServices.java
private void serviceDoneExecutingLocked(ServiceRecord r, boolean inDestroying,
boolean finishing) {
// ...
mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app);
// ...
}
当 Service 在指定时间内启动完成,就会调用 ActivityManagerService 的 Handler 移除炸弹。
- 2.3 引爆炸弹
/base/services/core/java/com/android/server/am/ActivityManagerService.java
final class MainHandler extends Handler {
public MainHandler(Looper looper) {
super(looper, null, true);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
// ...
case SERVICE_TIMEOUT_MSG: {
// ...
// Serivce在指定时间内没有执行完成,则会触发ANR
mServices.serviceTimeout((ProcessRecord)msg.obj);
} break;
}
}
/base/services/core/java/com/android/server/am/ActiveServices.java
// 处理Service超时
void serviceTimeout(ProcessRecord proc) {
// ANR 信息
String anrMessage = null;
synchronized(mAm) {
// ...
if (timeout != null && mAm.mLruProcesses.contains(proc)) {
StringWriter sw = new StringWriter();
PrintWriter pw = new FastPrintWriter(sw, false, 1024);
pw.println(timeout);
timeout.dump(pw, " ");
pw.close();
mLastAnrDump = sw.toString();
mAm.mHandler.removeCallbacks(mLastAnrDumpClearer);
mAm.mHandler.postDelayed(mLastAnrDumpClearer, LAST_ANR_LIFETIME_DURATION_MSECS);
anrMessage = "executing service " + timeout.shortName;
} else {
Message msg = mAm.mHandler.obtainMessage(
ActivityManagerService.SERVICE_TIMEOUT_MSG);
msg.obj = proc;
mAm.mHandler.sendMessageAtTime(msg, proc.execServicesFg
? (nextTime+SERVICE_TIMEOUT) : (nextTime + SERVICE_BACKGROUND_TIMEOUT));
}
}
if (anrMessage != null) {
// 显示 ANR 弹窗
mAm.mAppErrors.appNotResponding(proc, null, null, false, anrMessage);
}
}
代码中包括收集 ANR 信息,然后调用 ActivityManagerService 的 AppErrors 对象的 appNotResponding()。
/base/services/core/java/com/android/server/am/AppErrors.java
// app无相应,在AppErrors类中处理
final void appNotResponding(ProcessRecord app, ActivityRecord activity,
ActivityRecord parent, boolean aboveSystem, final String annotation) {
boolean isSilentANR;
// ...
synchronized (mService) {
// PowerManager.reboot() 会阻塞很长时间,因此忽略关机时的ANR
if (mService.mShuttingDown) {
Slog.i(TAG, "During shutdown skipping ANR: " + app + " " + annotation);
return;
} else if (app.notResponding) {
Slog.i(TAG, "Skipping duplicate ANR: " + app + " " + annotation);
return;
} else if (app.crashing) {
Slog.i(TAG, "Crashing app skipping ANR: " + app + " " + annotation);
return;
}
app.notResponding = true;
// 记录ANR到EventLog
EventLog.writeEvent(EventLogTags.AM_ANR, app.userId, app.pid,
app.processName, app.info.flags, annotation);
// 将当前进程添加到firstPids
firstPids.add(app.pid);
// 将system_server进程添加到firstPids
// 后台ANR的情况, 则直接杀掉
isSilentANR = !showBackground && !app.isInterestingToUserLocked() && app.pid != MY_PID;
if (!isSilentANR) {
//..
}
}
// 记录ANR输出到main log
StringBuilder info = new StringBuilder();
info.setLength(0);
info.append("ANR in ").append(app.processName);
if (activity != null && activity.shortComponentName != null) {
info.append(" (").append(activity.shortComponentName).append(")");
}
info.append("\n");
info.append("PID: ").append(app.pid).append("\n");
if (annotation != null) {
info.append("Reason: ").append(annotation).append("\n");
}
if (parent != null && parent != activity) {
info.append("Parent: ").append(parent.shortComponentName).append("\n");
}
// ...
synchronized (mService) {
mService.mBatteryStatsService.noteProcessAnr(app.processName, app.uid);
if (isSilentANR) {
// 后台anr,直接杀死app
app.kill("bg anr", true);
return;
}
// 设置app的ANR状态,病查询错误报告receiver
makeAppNotRespondingLocked(app,
activity != null ? activity.shortComponentName : null,
annotation != null ? "ANR " + annotation : "ANR",
info.toString());
// 弹出ANR对话框
Message msg = Message.obtain();
HashMap<String, Object> map = new HashMap<String, Object>();
msg.what = ActivityManagerService.SHOW_NOT_RESPONDING_UI_MSG;
msg.obj = map;
msg.arg1 = aboveSystem ? 1 : 0;
map.put("app", app);
if (activity != null) {
map.put("activity", activity);
}
// 向ui线程发送,内容为SHOW_NOT_RESPONDING_MSG的消息
mService.mUiHandler.sendMessage(msg);
}
}
代码中会判断各种条件,然后收集日志,最后弹出 ANR 窗口。
// 弹出 ANR 弹窗
void handleShowAnrUi(Message msg) {
Dialog d = null;
synchronized (mService) {
// ...
// If we've created a crash dialog, show it without the lock held
if (d != null) {
d.show();
}
}
3. 如何避免 ANR
如果产生了 ANR,如何分析呢,根据前面的代码,可以知道,ANR 的日志存放在 /data/anr/traces.txt 中,可以查看它来定位原因。
那么如何预防 ANR 呢?
- 避免在主线程上进行复杂耗时的操作,比如说发送接收网络数据/进行大量计算/操作数据库/读写文件等。这个可以通过使用AsyncTask 或者使用多线程来实现。
- BroadCastReceiver 要进行复杂操作的的时候,可以在 onReceive() 方法中启动一个 Service 来处理。
- 在设计及代码编写阶段避免出现出现同步/死锁或者错误处理不恰当等情况。