ANR is a big topic in Android. The four major components, Activity, Service, BroadCastReceiver, and ContentProvider are all involved. The four components have different monitoring mechanisms for Anr. BroadCastReceiver and Service are similar. Activity, as a strongly interactive component, has a unique Anr detection mechanism based on InputDispatcher, involving the JNI layer. Here we focus on how to set up Anr mechanism in Android system layer for Service components. The topics covered here include:
- The Anr monitoring mechanism for services is all about the heavy players
- What is the process of establishing Anr monitoring mechanism for Service
- What is the implementation principle of Anr monitoring mechanism of Service
When we start a Service, we call startService(), which is ContextImpl. ContextImpl is a subclass of Context, and it’s brother to ContextWrapper. It then calls its startServiceCommon() cross-process to the startService() method of ActivityManagerService (hereafter referred to as AMS) in the SYS_server process to request resources. In the AMS startService(), call startServiceLocked() in the ActiveServices class (hereafter referred to AS AS), Go through startServiceInnerLocked()->bringUpServiceLocked()->realStartServiceLocked() to actually execute the startService operation.
In realStartServiceLocked (), executed first bumpServiceExecutingLocked (), walk to the scheduleServiceTimeoutLocked () method, which is here, the monitoring mechanism of Anr, Through the Handler in AMS, a “delay bomb” is planted, and different “detonating time” is selected by distinguishing the front and back of Service. For ForegroundService, the timeout duration is 20 seconds. For background service, the timeout duration is 200 seconds
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);
}
Copy the code
At this point, the Anr mechanism has taken its first step in monitoring the Service startService by burying a delayed message (a delayed bomb). So what’s with the time-delayed bomb? In order to disarm the bomb in time, of course. If the bomb is not eventually removed within the delay time, the Anr will be triggered when the Handler is finally dispatched to the message. So that’s our overall guess for Anr. So let’s test this idea.
In the AS scheduleCreateService () method, the implementation of the bumpServiceExecutingLocked (), call the app. Thread. ScheduleCreateService () this sentence, Cross-process calls are made to the ApplicationThread class in the process that initiates startService, which is the internal class for ActivityThread and is responsible for cross-process communication through Binder mechanisms. The following is the content of the class execution scheduleCreateService() :
/ / ApplicationThread code:
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);
}
/ / ActivityThread code:
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);// Class H, inheriting Handler, specialized in receiving AMS scheduling messages from the four components.
}
/ / H code:
public void handleMessage(Message msg) {
switch(MSG. What) {.../ / to omit
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; ./ / to omit}}/ / ActivityThread code:
private void handleCreateService(CreateServiceData data) {.../ / to omit
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.onCreate();Call onCreate ()
mServices.put(data.token, service);
try {// The service was successfully created across processes.
ActivityManager.getService().serviceDoneExecuting(
data.token, SERVICE_DONE_EXECUTING_ANON, 0.0);
} catch (RemoteException e) {
throwe.rethrowFromSystemServer(); }}catch (Exception e) {
if(! mInstrumentation.onException(service, e)) {throw new RuntimeException(
"Unable to create service " + data.info.name
+ ":"+ e.toString(), e); }}}Copy the code
As you can see from the core code snippet above, the final Service creation process takes the form of AMS in the SYS_server process calling ApplicationThread across processes and sending messages to the App process via Handler. Execute handleCreateService(), call service.oncreate () and tell AMSserviceDoneExecuting() across processes. At this point, the Service is created in the App process. OnCreate () completes.
In this process, the bombs are being planted and time is slipping away. Moving on, we’ll look at how bombs are handled in AMS.
AMS is called the serviceDoneExecuting () method, is called AS the serviceDoneExecutingLocked (). In dealing with the Service. START_STICKY etc all at sixes and sevens after labeling, walked to the serviceDoneExecutingLocked () method, the real execution to “disarm bombs” buried after process, remove the time delay of news.
private void serviceDoneExecutingLocked(ServiceRecord r, boolean inDestroying,
boolean finishing) {
/ / to omit...
r.executeNesting--;
if (r.executeNesting <= 0) {
if(r.app ! =null) {
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE,
"Nesting at 0 of " + r.shortName);
r.app.execServicesFg = false;
r.app.executingServices.remove(r);
if (r.app.executingServices.size() == 0) {
if (DEBUG_SERVICE || DEBUG_SERVICE_EXECUTING) Slog.v(TAG_SERVICE_EXECUTING,
"No more executingServices of " + r.shortName);
// Defuse the bomb!!
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) {
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE,
"doneExecuting remove destroying " + r);
mDestroyingServices.remove(r);
r.bindings.clear();
}
mAm.updateOomAdjLocked(r.app, true);
}
r.executeFg = false;
if(r.tracker ! =null) {
r.tracker.setExecuting(false, mAm.mProcessStats.getMemFactorLocked(),
SystemClock.uptimeMillis());
if (finishing) {
r.tracker.clearCurrentOwner(r, false);
r.tracker = null; }}if (finishing) {
if(r.app ! =null && !r.app.persistent) {
r.app.services.remove(r);
if (r.whitelistManager) {
updateWhitelistManagerLocked(r.app);
}
}
r.app = null; }}}Copy the code
So the question comes, if the call mAm. MHandler. RemoveMessages (ActivityManagerService. SERVICE_TIMEOUT_MSG, of state Richard armitage pp); What happens when you find that there is no message? That means that the mam.mHandler has already processed the message. Let’s go inside mam.mHandler to see what it does when it receives this message.
final class MainHandler extends Handler {
public MainHandler(Looper looper) {
super(looper, null.true);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
/ /... omit
case SERVICE_TIMEOUT_MSG: {
mServices.serviceTimeout((ProcessRecord)msg.obj);
} break;
/ /... omit
}
/ /... omit
}
/ /... omit
Copy the code
It delegates serviceTimeout() to AS,
void serviceTimeout(ProcessRecord proc) {
String anrMessage = null;
synchronized(mAm) {
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);
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)); }}if(anrMessage ! =null) {
mAm.mAppErrors.appNotResponding(proc, null.null.false, anrMessage); }}Copy the code
The first half of the method collects Anr information, and the last sentence is the direct executor of the DETONATION of the Anr bomb. At this point, the Anr monitoring flow during Service creation is analyzed.
The three questions I mentioned in the opening paragraph are easily solved.
-
A: For services, the Anr monitoring process involves the communication between the App process and the Sys_Server process. The classes involved are ContextImpl, ActivityManagerService, ActiveServices, ApplicationThread, and ActivityThread, based on the order of execution, and use the Handler mechanism in their respective processes.
-
A: In the call process of onCreate, the flow of Anr mechanism can be represented by the following flow chart:
-
What is the implementation principle of Anr monitoring mechanism of Service
A: The IMPLEMENTATION principle of Anr monitoring is to send delayed messages based on the Handler message mechanism. Different Anr timeout times are used as the delay time of delayed messages. If the execution is completed within this time, remove the delayed message; Otherwise, the delayed message is triggered and the Anr reporting process is performed.