個(gè)人博客
Android中ANR的觸發(fā)機(jī)制-Service篇
概述
ANR,即Application Not Responding,應(yīng)用程序不響應(yīng)。在Android系統(tǒng)中咸灿,對于事件的處理,都需要在一定的時(shí)間內(nèi)完成,如果處理超時(shí)的話,就會(huì)觸發(fā)ANR豪嚎,彈出不響應(yīng)的界面,讓用戶選擇等待或是立即結(jié)束應(yīng)用谈火。ANR機(jī)制的簡單流程:在事件發(fā)給應(yīng)用處理前侈询,會(huì)先發(fā)一個(gè)延時(shí)消息到系統(tǒng)的Looper中,如果應(yīng)用在規(guī)定的時(shí)間內(nèi)執(zhí)行完成糯耍,則會(huì)移除掉延時(shí)消息扔字。如果沒有在規(guī)定時(shí)間內(nèi)執(zhí)行完,就會(huì)在處理延時(shí)消息中温技,觸發(fā)ANR革为。
ANR主要場景:
Service
BroadcastReceiver
ContentProvider
Input:包括輸入和觸摸
觸發(fā)機(jī)制分析
下面對Service進(jìn)行源碼分析,源碼為Android9.0舵鳞。
首先從Service的啟動(dòng)來分析震檩。這里只分析startService的模式,bindService模式暫時(shí)不分析蜓堕。
附一張時(shí)序圖
不管是通過Activity的startService還是非Activity的Context中的startService,最終都是調(diào)用ContextWrapper的startService方法:
//ContextWrapper
@Override
public ComponentName startService(Intent service) {
return mBase.startService(service);
}
mBase對應(yīng)的具體Context類為ContextImpl恳蹲。ContextImpl的startService方法:
//ContextImpl
@Override
public ComponentName startService(Intent service) {
warnIfCallingFromSystemProcess();
return startServiceCommon(service, false, mUser);
}
startService方法調(diào)用startServiceCommon方法
//ContextImpl
private ComponentName startServiceCommon(Intent service, boolean requireForeground,
UserHandle user) {
try {
validateServiceIntent(service);
service.prepareToLeaveProcess(this);
ComponentName cn = ActivityManager.getService().startService(
mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
getContentResolver()), requireForeground,
getOpPackageName(), user.getIdentifier());
if (cn != null) {
if (cn.getPackageName().equals("!")) {
throw new SecurityException(
"Not allowed to start service " + service
+ " without permission " + cn.getClassName());
} else if (cn.getPackageName().equals("!!")) {
throw new SecurityException(
"Unable to start service " + service
+ ": " + cn.getClassName());
} else if (cn.getPackageName().equals("?")) {
throw new IllegalStateException(
"Not allowed to start service " + service + ": " + cn.getClassName());
}
}
return cn;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
在這個(gè)方法里,跨進(jìn)程調(diào)用AMS的startService方法
//AMS
@Override
public ComponentName startService(IApplicationThread caller, Intent service,
String resolvedType, boolean requireForeground, String callingPackage, int userId)
throws TransactionTooLargeException {
enforceNotIsolatedCaller("startService");
// Refuse possible leaked file descriptors
if (service != null && service.hasFileDescriptors() == true) {
throw new IllegalArgumentException("File descriptors passed in Intent");
}
if (callingPackage == null) {
throw new IllegalArgumentException("callingPackage cannot be null");
}
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE,
"*** startService: " + service + " type=" + resolvedType + " fg=" + requireForeground);
synchronized(this) {
final int callingPid = Binder.getCallingPid();
final int callingUid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
ComponentName res;
try {
//調(diào)用ActiveServices的startServiceLocked方法
res = mServices.startServiceLocked(caller, service,
resolvedType, callingPid, callingUid,
requireForeground, callingPackage, userId);
} finally {
Binder.restoreCallingIdentity(origId);
}
return res;
}
}
AMS中調(diào)用ActiveServices的startServiceLocked方法:
//ActiveServices
ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
int callingPid, int callingUid, boolean fgRequired, String callingPackage, final int userId)
throws TransactionTooLargeException {
//...
ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
return cmp;
}
這個(gè)方法又調(diào)用了startServiceInnerLocked方法:
//ActiveServices
ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ServiceRecord r,
boolean callerFg, boolean addToStarting) throws TransactionTooLargeException {
//...
String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false, false);
if (error != null) {
return new ComponentName("!!", error);
}
//...
}
這個(gè)方法調(diào)用bringUpServiceLocked方法:
//ActiveServices
private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,
boolean whileRestarting, boolean permissionsReviewRequired)
throws TransactionTooLargeException {
//...
realStartServiceLocked(r, app, execInFg);
//...
}
這個(gè)方法里調(diào)用realStartServiceLocked方法:
//ActiveServices
private final void realStartServiceLocked(ServiceRecord r,
ProcessRecord app, boolean execInFg) throws RemoteException {
//...
bumpServiceExecutingLocked(r, execInFg, "create");
//...
app.thread.scheduleCreateService(r, r.serviceInfo,
mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
app.repProcState);
//...
}
這里先調(diào)用了bumpServiceExecutingLocked方法用來設(shè)置超時(shí)消息:
//ActiveServices
private final void bumpServiceExecutingLocked(ServiceRecord r, boolean fg, String why) {
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, ">>> EXECUTING "
+ why + " of " + r + " in app " + r.app);
else if (DEBUG_SERVICE_EXECUTING) Slog.v(TAG_SERVICE_EXECUTING, ">>> EXECUTING "
+ why + " of " + r.shortName);
// For b/34123235: Services within the system server won't start until SystemServer
// does Looper.loop(), so we shouldn't try to start/bind to them too early in the boot
// process. However, since there's a little point of showing the ANR dialog in that case,
// let's suppress the timeout until PHASE_THIRD_PARTY_APPS_CAN_START.
//
// (Note there are multiple services start at PHASE_THIRD_PARTY_APPS_CAN_START too,
// which technically could also trigger this timeout if there's a system server
// that takes a long time to handle PHASE_THIRD_PARTY_APPS_CAN_START, but that shouldn't
// happen.)
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) {
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;
}
這個(gè)方法中調(diào)用scheduleServiceTimeoutLocked方法:
//ActiveServices
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);
}
調(diào)用ActivityManagerService的MainHandler發(fā)送一個(gè)SERVICE_TIMEOUT消息俩滥,這里具體根據(jù)是否為前臺消息發(fā)送的消息不同嘉蕾。
前臺服務(wù)超時(shí)時(shí)間:20s,后臺服務(wù)超時(shí)時(shí)間:200s
// 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;
設(shè)置超時(shí)消息就先看到這里,接著看啟動(dòng)Service
調(diào)用ProcessRecord中的IApplicationThread類型的thread屬性的scheduleCreateService方法霜旧,即調(diào)用到了ActivityThread的內(nèi)部類ApplicationThread的scheduleCreateService方法:
//ActivityThread$ApplicationThrad
public final void scheduleCreateService(IBinder token,
ServiceInfo info, CompatibilityInfo compatInfo, int processState) {
updateProcessState(processState, false);
CreateServiceData s = new CreateServiceData();
s.token = token;
s.info = info;
s.compatInfo = compatInfo;
sendMessage(H.CREATE_SERVICE, s);
}
這個(gè)方法里調(diào)用了ActivityThread的sendMessage方法:
//ActivityThread
void sendMessage(int what, Object obj) {
sendMessage(what, obj, 0, 0, false);
}
最終通過Handler發(fā)送了一個(gè)消息出去
//Handler
private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) {
if (DEBUG_MESSAGES) Slog.v(
TAG, "SCHEDULE " + what + " " + mH.codeToString(what)
+ ": " + arg1 + " / " + obj);
Message msg = Message.obtain();
msg.what = what;
msg.obj = obj;
msg.arg1 = arg1;
msg.arg2 = arg2;
if (async) {
msg.setAsynchronous(true);
}
mH.sendMessage(msg);
}
在mH的handleMessage中回調(diào)處理:
//ActivityThread$H.handleMessage
case CREATE_SERVICE:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, ("serviceCreate: " + String.valueOf(msg.obj)));
handleCreateService((CreateServiceData)msg.obj);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
//ActivityThread
private void handleCreateService(CreateServiceData data) {
// If we are getting ready to gc after going to the background, well
// we are back active so skip it.
unscheduleGcIdler();
LoadedApk packageInfo = getPackageInfoNoCheck(
data.info.applicationInfo, data.compatInfo);
Service service = null;
try {
java.lang.ClassLoader cl = packageInfo.getClassLoader();
service = packageInfo.getAppFactory()
.instantiateService(cl, data.info.name, data.intent);
} catch (Exception e) {
if (!mInstrumentation.onException(service, e)) {
throw new RuntimeException(
"Unable to instantiate service " + data.info.name
+ ": " + e.toString(), e);
}
}
try {
if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);
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的創(chuàng)建错忱,并回調(diào)onCreate方法
service.onCreate();
mServices.put(data.token, service);
try {
//通知移除延時(shí)消息
ActivityManager.getService().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);
}
}
}
如果在規(guī)定的時(shí)間內(nèi)完成處理,則會(huì)調(diào)用AMS的serviceDoneExecuting:
//AMS
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);
}
}
這個(gè)方法調(diào)用ActiveServices中的serviceDoneExecutingLocked方法:
//ActiveServices
void serviceDoneExecutingLocked(ServiceRecord r, int type, int startId, int res) {
//...
serviceDoneExecutingLocked(r, inDestroying, inDestroying);
//...
}
這個(gè)方法會(huì)調(diào)用另一個(gè)重載方法:
//ActiveServices
private void serviceDoneExecutingLocked(ServiceRecord r, boolean inDestroying,
boolean finishing) {
//...
mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app);
//...
}
在這個(gè)方法里移除了前面的延時(shí)消息挂据,就不會(huì)觸發(fā)ANR以清。
如果沒有及時(shí)移除這個(gè)消息,那么將會(huì)在ActivityManagerService的MainHandler中觸發(fā):
MainHandler收到消息的處理:
//ActivityManagerService$MainHandler.handleMessage
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
//...
case SERVICE_TIMEOUT_MSG: {
mServices.serviceTimeout((ProcessRecord)msg.obj);
} break;
//...
}
}
這個(gè)方法調(diào)用ActiveServices中的serviceTimeout方法:
void serviceTimeout(ProcessRecord proc) {
//...
if (anrMessage != null) {
mAm.mAppErrors.appNotResponding(proc, null, null, false, anrMessage);
}
}
在這里彈出了不響應(yīng)的界面崎逃。