📜 ⬆️ ⬇️

Causes of ANR and how to avoid it

ANR (Application Not Responding) - an error that occurs when an application does not respond. As a result, a dialog opens prompting the user to wait or close the application.
image alt

ANR conditions



Normally the main thread is blocked.

If you read my articles, you probably already got used to the fact that we are crawling into the source code. So let's see what ANR looks like under the hood .

The class AppErrors deals with processing not only ANR, but also other errors that may occur in the application, including crash. The handleShowAnrUi () method just opens this scary window for many developers and users, displaying ANR.

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(); } } ... 

However, ANR does not start here. As I said above, one of the first causes of this error is the delay in the input event, which is 5 seconds. With a short search, we can find where this value is given.

 namespace android { // Default input dispatching timeout if there is no focused application or paused window // from which to determine an appropriate dispatching timeout. const nsecs_t DEFAULT_INPUT_DISPATCHING_TIMEOUT = 5000 * 1000000LL; // 5 sec 

Now we can see in the code where the native part is called. This happens in the InputManagerService class.

  // Native callback. private long notifyANR(InputApplicationHandle inputApplicationHandle, InputWindowHandle inputWindowHandle, String reason) { return mWindowManagerCallbacks.notifyANR( inputApplicationHandle, inputWindowHandle, reason); } 

And here is the mWindowManagerCallbacks in InputMonitor :

  if (appWindowToken != null && appWindowToken.appToken != null) { // Notify the activity manager about the timeout and let it decide whether // to abort dispatching or keep waiting. final AppWindowContainerController controller = appWindowToken.getController(); final boolean abort = controller != null && controller.keyDispatchingTimedOut(reason, (windowState != null) ? windowState.mSession.mPid : -1); if (!abort) { // The activity manager declined to abort dispatching. // Wait a bit longer and timeout again later. return appWindowToken.mInputDispatchingTimeoutNanos; } } else if (windowState != null) { try { // Notify the activity manager about the timeout and let it decide whether // to abort dispatching or keep waiting. long timeout = ActivityManager.getService().inputDispatchingTimedOut( windowState.mSession.mPid, aboveSystem, reason); if (timeout >= 0) { // The activity manager declined to abort dispatching. // Wait a bit longer and timeout again later. return timeout * 1000000L; // nanoseconds } } catch (RemoteException ex) { } } return 0; // abort dispatching } 

Let's take a closer look at inputDispatchingTimedOut (). This is where we show the message through the ActivityManager about the timeout and let the user decide whether to cancel the action or continue waiting. And it is in ActivityManagerService that AppErrors is called in case of a crash or ANR.

  private boolean makeAppCrashingLocked(ProcessRecord app, String shortMsg, String longMsg, String stackTrace) { app.crashing = true; app.crashingReport = generateProcessError(app, ActivityManager.ProcessErrorStateInfo.CRASHED, null, shortMsg, longMsg, stackTrace); startAppProblemLocked(app); app.stopFreezingAllLocked(); return handleAppCrashLocked(app, shortMsg, longMsg, stackTrace); } private void makeAppNotRespondingLocked(ProcessRecord app, String activity, String shortMsg, String longMsg) { app.notResponding = true; app.notRespondingReport = generateProcessError(app, ActivityManager.ProcessErrorStateInfo.NOT_RESPONDING, activity, shortMsg, longMsg, null); startAppProblemLocked(app); app.stopFreezingAllLocked(); } 

ANR root causes



How to avoid ANR



How to catch ANR



PS I publish all the selections in the @paradisecurity telegram.

Source: https://habr.com/ru/post/439086/