Ursachen von ANR und wie man es vermeidet

ANR (Anwendung antwortet nicht) - Ein Fehler, der auftritt, wenn die Anwendung nicht antwortet. Infolgedessen wird ein Dialogfeld geöffnet, in dem der Benutzer aufgefordert wird, die Anwendung zu warten oder zu schließen.
Bild alt

ANR-Bedingungen


  • Eingabeereignisse (Tasten und Berührungsereignisse) werden 5 Sekunden lang nicht verarbeitet.
  • BroadcastReceiver (onRecieve ()) wurde nicht innerhalb der angegebenen Zeit verarbeitet (Vordergrund - 10 s, Hintergrund - 60 s);
  • ContentProvider nicht innerhalb von 10 Sekunden abgeschlossen.

Normalerweise ist der Haupt-Thread blockiert.

Wenn Sie meine Artikel lesen, sind Sie wahrscheinlich bereits daran gewöhnt, dass wir in den Quellcode kriechen. Mal sehen, wie der ANR unter der Haube aussieht.

Die AppErrors- Klasse behandelt nicht nur ANR, sondern auch andere Fehler, die in der Anwendung auftreten können, einschließlich Absturz. Die handleShowAnrUi () -Methode öffnet gerade dieses beängstigende Fenster für viele Entwickler und Benutzer, in dem ANR angezeigt wird.

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

ANR startet hier jedoch nicht. Wie ich oben sagte, ist eine der ersten Ursachen für diesen Fehler die Verzögerung des Eingabeereignisses, die 5 Sekunden beträgt. Durch eine kurze Suche können wir herausfinden, wo dieser Wert eingestellt ist.

 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 

Jetzt können wir in den Code schauen, in dem der native Teil aufgerufen wird. Dies geschieht in der Klasse InputManagerService .

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

Und hier sind die mWindowManagerCallbacks im 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 } 

Schauen wir uns inputDispatchingTimedOut () genauer an. Hier zeigen wir einfach die Nachricht über den ActivityManager über den Ablauf des Timeouts und lassen den Benutzer entscheiden, ob die Aktion abgebrochen oder weiter gewartet werden soll. Und im ActivityManagerService wird AppErrors im Falle eines Absturzes oder einer ANR aufgerufen.

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

Die Hauptursachen von ANR


  • Eingangs- / Ausgangssperre
  • Überlastung des Netzwerks
  • Fadenblockierung
  • Endlosschleife
  • Geschäftslogik dauert zu lange

ANR vermeiden


  • Der Hauptthread der Benutzeroberfläche führt eine Logik aus, die nur der Benutzeroberfläche zugeordnet ist.
  • Komplexe Berechnungen (z. B. Datenbankoperationen, Eingabe-Ausgabe-Operationen, Netzwerkoperationen usw.) werden in einem separaten Stream ausgeführt.
  • Verwenden Sie den Handler, um zwischen dem Benutzeroberflächenthread und dem Workflow zu interagieren.
  • Verwenden Sie RxJava usw. asynchrone Operationen zu behandeln.

Wie man ANR fängt


  • Informationen zu ANR können in der Datei /data/anr/traces.txt oder in einem anderen Pfad / data / anr / anr_ * gespeichert werden. Sie können es mit den folgenden Befehlen erhalten:

     adb root adb shell ls /data/anr adb pull /data/anr/<filename> 
  • Verwenden Sie das Open Source-Projekt ANR-WatchDog , um ANR zu erkennen
  • Siehe Vermeiden von ANR :)

PS Ich veröffentliche alle Auswahlen im Telegrammkanal @paradisecurity .

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


All Articles