分类
Android 的ANR 主要可以分为四种
- 键盘无响应 (Activity超时)
- service启动超时
- BroadcastReceiver 处理超时
- ContentProvider 处理超时
超时定义
多次时间算超时? ContentProvider、BroadcastReceiver、键盘无响应 在 ActivityManagerService中已经定义好了
service的超时定义在ActiveServices中
public class ActivityManagerService{
...
// How long we wait for an attached process to publish its content providers
// before we decide it must be hung.
static final int CONTENT_PROVIDER_PUBLISH_TIMEOUT = 10*1000;
// How long we wait until we timeout on key dispatching.
static final int KEY_DISPATCHING_TIMEOUT = 5*1000;
// How long we allow a receiver to run before giving up on it.
static final int BROADCAST_FG_TIMEOUT = 10*1000;
static final int BROADCAST_BG_TIMEOUT = 60*1000;
}
public class ActiveService{
...
// How long we wait for a service to finish executing.
static final int SERVICE_TIMEOUT = 20*1000;
// How long we wait for a service to finish executing.
static final int SERVICE_BACKGROUND_TIMEOUT = SERVICE_TIMEOUT * 10;
// How long the startForegroundService() grace period is to get around to
// calling startForeground() before we ANR + stop it.
static final int SERVICE_START_FOREGROUND_TIMEOUT = 10*1000;
}
可以看到 CONTENT_PROVIDER 发布时间时间阈值 是10s
input分发阈值是5s
前台广播和后台广播的超时时间不一样 分别为10s 和60s
前台service和后台service也不一样 分别为 10s和200s
如何触发的ANR
Service
当我们调用startService的时候 会从ActivityThread产生一个到AMS的binder调用 。调用用AMS的startService
public class ActivityManagerService{
@Override
public ComponentName startService(IApplicationThread caller, Intent service,
String resolvedType, boolean requireForeground, String callingPackage, int userId)
throws TransactionTooLargeException {
enforceNotIsolatedCaller("startService");
...
synchronized(this) {
final int callingPid = Binder.getCallingPid();
final int callingUid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
ComponentName res;
try {
// 这个mServices 是ActiveService的实例
res = mServices.startServiceLocked(caller, service,
resolvedType, callingPid, callingUid,
requireForeground, callingPackage, userId);
} finally {
Binder.restoreCallingIdentity(origId);
}
return res;
}
}
}
经过ActiveService的一系列调用,最终会进入ActiveService -> realStartServiceLocked()方法
public class ActiveService{
private final void realStartServiceLocked(ServiceRecord r,
ProcessRecord app, boolean execInFg) throws RemoteException {
...
bumpServiceExecutingLocked(r, execInFg, "create");
...
// 通知app线程启动service
app.thread.scheduleCreateService(r, r.serviceInfo,
mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
app.repProcState);
}
private final void bumpServiceExecutingLocked(ServiceRecord r, boolean fg, String why) {
boolean timeoutNeeded = true;
if ((mAm.mBootPhase < SystemService.PHASE_THIRD_PARTY_APPS_CAN_START)
&& (r.app != null) && (r.app.pid == android.os.Process.myPid())) {
Slog.w(TAG, "Too early to start/bind service in system_server: Phase=" + mAm.mBootPhase
+ " " + r.getComponentName());
timeoutNeeded = false;
}
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 (timeoutNeeded && r.app.executingServices.size() == 1) {
// 定时任务 判断service是否超时
scheduleServiceTimeoutLocked(r.app);
}
}
} else if (r.app != null && fg && !r.app.execServicesFg) {
r.app.execServicesFg = true;
if (timeoutNeeded) {
scheduleServiceTimeoutLocked(r.app);
}
}
r.executeFg |= fg;
r.executeNesting++;
r.executingStart = now;
}
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);
}
}
在这个方法的入口出会调用 bumpServiceExecutingLocked 接着调用 scheduleServiceTimeoutLocked 从方法名字便能看出 这边shedule了一个判断service是否超时的任务
scheduleServiceTimeoutLocked 通过ActivityManagerService的Handler发送了一个SERVICE_TIMEOUT_MSG 的消息 。注意这个延时时间,根据service的前后台状态有所不同。我们得去看看 AMS是怎么处理这个消息的。
临时总结下:启动service的时候 ActiveService会发送一个定时任务 用于判断 service是否超时至于怎么判断的 后面再看。
ActivityManagerService -> MainHandler
final class MainHandler extends Handler {
public void handleMessage(Message msg) {
switch (msg.what) {
case SERVICE_TIMEOUT_MSG:
mServices.serviceTimeout((ProcessRecord)msg.obj);
break;
}
}
}
AMS 中的Handler处理又调回了ActiveService
public class ActiveService{
void serviceTimeout(ProcessRecord proc) {
String anrMessage = null;
synchronized(mAm) {
if (proc.executingServices.size() == 0 || proc.thread == null) {
return;
}
final long now = SystemClock.uptimeMillis();
// 因为这个任务是SERVICE_TIMEOUT或者SERVICE_BACKGROUND_TIMEOUT时间后才会执行 所以 这边计算出一个server启动的最大时间
final long maxTime = now -
(proc.execServicesFg ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);
ServiceRecord timeout = null;
long nextTime = 0;
// 遍历所有正在执行的service
for (int i=proc.executingServices.size()-1; i>=0; i--) {
ServiceRecord sr = proc.executingServices.valueAt(i);
if (sr.executingStart < maxTime) {
// 超时
timeout = sr;
break;
}
if (sr.executingStart > nextTime) {
nextTime = sr.executingStart;
}
}
if (timeout != null && mAm.mLruProcesses.contains(proc)) {
// 如果超时 则记录超时日志
Slog.w(TAG, "Timeout executing service: " + timeout);
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));
}
}
// ANR
if (anrMessage != null) {
mAm.mAppErrors.appNotResponding(proc, null, null, false, anrMessage);
}
}
}
可以看到 超时的时候会调用 mAm.mAppErrors.appNotResponding 响应ANR。但是未超时的时候,却是重新设置了一个定时任务去做检查,这样岂不是白白消耗性能。
其实不是的,当service启动成功后,会把这个定时任务取消。总的逻辑就是 启动的时候发送一个定时任务检查是否ANR,如果service在定时任务执行之前启动成功,定时任务就会被删除,否者就会执行定时任务检查是否ANR。
mAm.mAppErrors.appNotResponding(proc, null, null, false, anrMessage); 会发送一个消息给AMS的Handler
case SHOW_NOT_RESPONDING_UI_MSG: {
mAppErrors.handleShowAnrUi(msg);
ensureBootCompleted();
}
public class AppErrors{
void handleShowAnrUi(Message msg) {
Dialog dialogToShow = null;
synchronized (mService) {
AppNotRespondingDialog.Data data = (AppNotRespondingDialog.Data) msg.obj;
final ProcessRecord proc = data.proc;
if (proc == null) {
Slog.e(TAG, "handleShowAnrUi: proc is null");
return;
}
if (proc.anrDialog != null) {
Slog.e(TAG, "App already has anr dialog: " + proc);
MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_APP_ANR,
AppNotRespondingDialog.ALREADY_SHOWING);
return;
}
Intent intent = new Intent("android.intent.action.ANR");
if (!mService.mProcessesReady) {
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
| Intent.FLAG_RECEIVER_FOREGROUND);
}
mService.broadcastIntentLocked(null, null, intent,
null, null, 0, null, null, null, AppOpsManager.OP_NONE,
null, false, false, MY_PID, Process.SYSTEM_UID, 0 /* TODO: Verify */);
boolean showBackground = Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0;
if (mService.canShowErrorDialogs() || showBackground) {
dialogToShow = new AppNotRespondingDialog(mService, mContext, data);
proc.anrDialog = dialogToShow;
} else {
MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_APP_ANR,
AppNotRespondingDialog.CANT_SHOW);
// Just kill the app if there is no dialog to be shown.
mService.killAppAtUsersRequest(proc, null);
}
}
// If we've created a crash dialog, show it without the lock held
if (dialogToShow != null) {
dialogToShow.show();
}
}
}
handleShowAnrUi 弹出一个dialog 即我们看到的anr弹框
以上就是ANR产生逻辑,再来看看service启动成功后 定时消息是怎么移除的 ActiveService -> realStartServiceLocked 会调用 app.thread.scheduleCreateService 通知app进程启动 service 。在ActivityThread中会 执行 handleCreateService方法
private void handleCreateService(CreateServiceData data) {
unscheduleGcIdler();
LoadedApk packageInfo = getPackageInfoNoCheck(
data.info.applicationInfo, data.compatInfo);
Service service = null;
try {
// 通过反射 实例化Service
java.lang.ClassLoader cl = packageInfo.getClassLoader();
service = packageInfo.getAppFactory()
.instantiateService(cl, data.info.name, data.intent);
} catch (Exception e) {
...
}
try {
ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
context.setOuterContext(service);
Application app = packageInfo.makeApplication(false, mInstrumentation);
service.attach(context, this, data.info.name, data.token, app,
ActivityManager.getService());
service.onCreate();
mServices.put(data.token, service);
try {
//启动成功 通知 AMS 客户端的Serivce已经启动成功了
//调用回到AMS 的serviceDoneExecuting方法
ActivityManager.getService().serviceDoneExecuting(
data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
} catch (Exception e) {
...
}
}
public class ActivityManagerService{
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");
}
// 这个mService是ActiveService
mServices.serviceDoneExecutingLocked((ServiceRecord)token, type, startId, res);
}
}
}
public class ActiveService{
void serviceDoneExecutingLocked(ServiceRecord r, int type, int startId, int res) {
boolean inDestroying = mDestroyingServices.contains(r);
if (r != null) {
...
final long origId = Binder.clearCallingIdentity();
serviceDoneExecutingLocked(r, inDestroying, inDestroying);
Binder.restoreCallingIdentity(origId);
} else {
Slog.w(TAG, "Done executing unknown service from pid "
+ Binder.getCallingPid());
}
}
private void serviceDoneExecutingLocked(ServiceRecord r, boolean inDestroying,
boolean finishing) {
r.executeNesting--;
if (r.executeNesting <= 0) {
if (r.app != null) {
r.app.execServicesFg = false;
r.app.executingServices.remove(r);
if (r.app.executingServices.size() == 0) {
// 重点 如果service全部启动成功,则移除SERVICE_TIMEOUT_MSG的定时任务
mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app);
} else if (r.executeFg) {
// Need to re-evaluate whether the app still needs to be in the foreground.
for (int i=r.app.executingServices.size()-1; i>=0; i--) {
if (r.app.executingServices.valueAt(i).executeFg) {
r.app.execServicesFg = true;
break;
}
}
}
if (inDestroying) {
mDestroyingServices.remove(r);
r.bindings.clear();
}
mAm.updateOomAdjLocked(r.app, true);
}
}
}
}
总结起来就是 Service启动的时候 产生一个Binder调用,调用AMS的 startSerivce ,接着调用ActiveSerivce的realStartServiceLocked,然后将会发送一个定时任务到AMS,在定时任务中将会判断是否ANR ;如果Service在定时任务之前启动成功,则定时任务会被移除,也就不会产生ANR了