catalogue
Display principle of ANR pop-up window
Consider an interview question. A Service runs in an independent process. Will time-consuming operations in the onCreate method of the Service cause ANR?
Direct conclusion: Yes, but there will be no pop-up window of ANR.
Basics:
Four scenarios of ANR:
-
Service TimeOut: The service is not completed within the specified time: the foreground service is 20s and the background service is 200s
-
BroadCastQueue TimeOut: the broadcast is not processed within the specified time: within 10s of the foreground broadcast and 60s of the background broadcast
-
ContentProvider TimeOut: publish was not completed within 10s
-
Input Dispatching timeout: No response to keyboard input, touch screen and other events within 5s
The root cause of ANR is that the application does not process the tasks specified by AMS within the specified time.
Therefore, the Service is not completed within the specified time, and the Service that is not the main process still needs to communicate through AMS. This also shows that ANR will be generated.
experiment:
In theory, yes, let's write a small demo to try.
Write a Service:
class NoZuoNoDieService: Service() { override fun onBind(intent: Intent?): IBinder? { return null } override fun onCreate() { super.onCreate() // Naps are time-consuming Thread.sleep(21_000) } }
Then declare the independent process in the AndroidManifest
<service android:name=".service.NoZuoNoDieService" android:process=":whatever" />
Finally, we put this Service up to die
startService(Intent(this, NoZuoNoDieService::class.java))
There is no ANR pop-up window in the application, but logcat has ANR related information. Take a look at the trace file:
----- pid 14608 at 2021-03-23 19:56:20 ----- Cmd line: demo.com.sam.demofactory:whatever ... Omit irrelevant information "main" prio=5 tid=1 Sleeping | group="main" sCount=1 dsCount=0 flags=1 obj=0x73f13a80 self=0x78e7cc2a00 | sysTid=14608 nice=0 cgrp=default sched=0/0 handle=0x796c96d9a8 | state=S schedstat=( 57816925 3067496 80 ) utm=2 stm=3 core=4 HZ=100 | stack=0x7fe1d03000-0x7fe1d05000 stackSize=8MB | held mutexes= at java.lang.Thread.sleep(Native method) - sleeping on <0x0a71f3e8> (a java.lang.Object) at java.lang.Thread.sleep(Thread.java:373) - locked <0x0a71f3e8> (a java.lang.Object) at java.lang.Thread.sleep(Thread.java:314) at demo.com.sam.demofactory.service.NoZuoNoDieService.onCreate(NoZuoNoDieService.kt:15) at android.app.ActivityThread.handleCreateService(ActivityThread.java:3426) at android.app.ActivityThread.-wrap4(ActivityThread.java:-1) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1744) at android.os.Handler.dispatchMessage(Handler.java:106) at android.os.Looper.loop(Looper.java:171) at android.app.ActivityThread.main(ActivityThread.java:6611) at java.lang.reflect.Method.invoke(Native method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
Display principle of ANR pop-up window
Let's see how this ANR without pop-up window happens
First, let's review Tell me about the Service startup process
AMS actually starts the Service by calling the ActiveServices.realStartServiceLocked method:
// API 29 com.android.server.am.ActiveServices private final void realStartServiceLocked(ServiceRecord r, ProcessRecord app, boolean execInFg) throws RemoteException { ... bumpServiceExecutingLocked(r, execInFg, "create"); } private final void bumpServiceExecutingLocked(ServiceRecord r, boolean fg, String why) { ... scheduleServiceTimeoutLocked(r.app); } // Delay sending a message through the Handler, and the delay time is the ANR time triggered by the Service // SERVICE_TIMEOUT = 20*1000 // SERVICE_BACKGROUND_TIMEOUT = SERVICE_TIMEOUT * 10; 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); }
If the Service is started and completed within the specified time, this message will be remove d. Today, we'll see how to handle this message after timeout.
// com.android.server.am.ActivityManagerService.MainHandler final class MainHandler extends Handler { @Override public void handleMessage(Message msg) { switch (msg.what) { case SERVICE_TIMEOUT_MSG: { mServices.serviceTimeout((ProcessRecord)msg.obj); } break; } }
mServices is ActiveServices:
// com.android.server.am.ActiveServices#serviceTimeout void serviceTimeout(ProcessRecord proc) { ... if (timeout != null && mAm.mProcessList.mLruProcesses.contains(proc)) { Slog.w(TAG, "Timeout executing service: " + timeout); ... mAm.mHandler.removeCallbacks(mLastAnrDumpClearer); mAm.mHandler.postDelayed(mLastAnrDumpClearer, LAST_ANR_LIFETIME_DURATION_MSECS); anrMessage = "executing service " + timeout.shortInstanceName; } ... if (anrMessage != null) { proc.appNotResponding(null, null, null, null, false, anrMessage); } }
appNotResponding is the code that finally handles the ANR logic
// com.android.server.am.ProcessRecord void appNotResponding(String activityShortComponentName, ApplicationInfo aInfo, ...) { ... if (isMonitorCpuUsage()) { mService.updateCpuStatsNow(); } ... if (isSilentAnr() && !isDebugging()) { kill("bg anr", true); return; } // Bring up the infamous App Not Responding dialog Message msg = Message.obtain(); msg.what = ActivityManagerService.SHOW_NOT_RESPONDING_UI_MSG; msg.obj = new AppNotRespondingDialog.Data(this, aInfo, aboveSystem); mService.mUiHandler.sendMessage(msg); }
If isSilentAnr() is true, the process will be killed directly and the message displaying ANR pop-up window will not be sent. Let's look at the relevant methods
boolean isSilentAnr() { return !getShowBackground() && !isInterestingForBackgroundTraces(); } private boolean getShowBackground() { return Settings.Secure.getInt(mService.mContext.getContentResolver(), Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0; } private boolean isInterestingForBackgroundTraces() { // The system_server is always considered interesting. if (pid == MY_PID) { return true; } // A package is considered interesting if any of the following is true : // // - It's displaying an activity. // - It's the SystemUI. // - It has an overlay or a top UI visible. // // NOTE: The check whether a given ProcessRecord belongs to the systemui // process is a bit of a kludge, but the same pattern seems repeated at // several places in the system server. return isInterestingToUserLocked() || (info != null && "com.android.systemui".equals(info.packageName)) || (hasTopUi() || hasOverlayUi()); }
We draw two conclusions:
-
ANR_ SHOW_ Backgroup can configure and display all "application unresponsive" in "developer options". After opening, even this scenario will have an anr pop-up window
-
If the above configuration is not enabled, the ANR pop-up window will not be displayed if it is a background process