Eine Android-App stürzt ab, wenn es zu einem unerwarteten Beenden kommt, das durch eine unbehandelte Ausnahme oder ein unbehandeltes Signal verursacht wird. Eine App, die in Java oder Kotlin geschrieben wurde, stürzt ab, wenn sie eine unbehandelte Ausnahme auslöst, die durch die Klasse Throwable dargestellt wird. Eine App, die mit Maschinencode oder C++ geschrieben wurde, stürzt ab, wenn während der Ausführung ein nicht behandeltes Signal wie SIGSEGV auftritt.
Wenn eine App abstürzt, beendet Android den Prozess der App und zeigt ein Dialogfeld an, um den Nutzer darüber zu informieren, dass die App beendet wurde (siehe Abbildung 1).
Eine App muss nicht im Vordergrund ausgeführt werden, damit sie abstürzt. Jede App-Komponente, auch Komponenten wie Broadcast-Empfänger oder Content-Provider, die im Hintergrund ausgeführt werden, kann zum Absturz einer App führen. Diese Abstürze sind für Nutzer oft verwirrend, da sie die App nicht aktiv verwendet haben.
Wenn Ihre App abstürzt, können Sie das Problem mithilfe der Informationen auf dieser Seite diagnostizieren und beheben.
Problem erkennen
Sie wissen möglicherweise nicht immer, dass Ihre Nutzer bei der Verwendung Ihrer App Abstürze erleben. Wenn Sie Ihre App bereits veröffentlicht haben, können Sie die Absturzraten für Ihre App in Android Vitals einsehen.
Android Vitals
Mit Android Vitals können Sie die Absturzrate Ihrer App im Blick behalten und verbessern. Bei Android Vitals werden verschiedene Absturzraten gemessen:
- Absturzrate:Prozentsatz der aktiven Nutzer pro Tag, bei denen ein beliebiger Absturz aufgetreten ist.
Rate der vom Nutzer wahrgenommenen Abstürze:Prozentsatz der aktiven Nutzer pro Tag, bei denen mindestens ein Absturz aufgetreten ist, während sie Ihre App aktiv verwendet haben (ein vom Nutzer wahrgenommener Absturz). Eine App gilt als aktiv verwendet, wenn sie eine Aktivität anzeigt oder einen Dienst im Vordergrund ausführt.
Mehrfachabsturzrate:Prozentsatz der aktiven Nutzer pro Tag, bei denen mindestens zwei Abstürze aufgetreten sind.
Ein aktiver Nutzer pro Tag ist ein einzelner Nutzer, der Ihre App an einem Tag auf einem Gerät verwendet, möglicherweise in mehreren Sitzungen. Wenn ein Nutzer Ihre App an einem Tag auf mehreren Geräten verwendet, wird jedes Gerät zur Zahl der aktiven Nutzer an diesem Tag addiert. Wenn mehrere Nutzer an einem Tag dasselbe Gerät verwenden, zählt dies als ein aktiver Nutzer.
Die Rate der vom Nutzer wahrgenommenen Abstürze ist ein Vitalparameter, d. h., er beeinflusst die Sichtbarkeit Ihrer App bei Google Play. Das ist wichtig, weil die gezählten Abstürze immer auftreten, während der Nutzer mit der App interagiert, und daher die größte Störung verursachen.
Für diesen Messwert hat Google Play zwei Grenzwerte zu unerwünschtem Verhalten definiert:
- Grenzwert zu unerwünschtem Verhalten insgesamt: Bei mindestens 1, 09% der aktiven Nutzer pro Tag tritt auf allen Gerätemodellen ein vom Nutzer wahrgenommener Absturz auf.
- Grenzwert zu unerwünschtem Verhalten auf einzelnen Geräten:Bei mindestens 8% der aktiven Nutzer pro Tag tritt bei einem einzelnen Gerätemodell ein vom Nutzer wahrgenommener Absturz auf.
Wenn Ihre App den Grenzwert zu unerwünschtem Verhalten insgesamt überschreitet, ist sie wahrscheinlich auf allen Geräten weniger gut sichtbar. Wenn Ihre App auf einigen Geräten den Grenzwert für unerwünschtes Verhalten pro Gerät überschreitet, ist sie auf diesen Geräten wahrscheinlich weniger gut sichtbar und in Ihrem Store-Eintrag wird möglicherweise eine Warnung angezeigt.
Android Vitals kann Sie in der Play Console benachrichtigen, wenn Ihre App übermäßig viele Abstürze aufweist.
Informationen dazu, wie Google Play Android Vitals-Daten erhebt, finden Sie in der Play Console-Dokumentation.
Abstürze diagnostizieren
Wenn Sie festgestellt haben, dass Ihre App Abstürze meldet, müssen Sie als Nächstes die Ursachen dafür ermitteln. Das Beheben von Abstürzen kann schwierig sein. Wenn Sie die Ursache des Absturzes ermitteln können, finden Sie höchstwahrscheinlich auch eine Lösung.
Es gibt viele Situationen, die zu einem Absturz Ihrer App führen können. Einige Gründe sind offensichtlich, z. B. die Prüfung auf einen Nullwert oder einen leeren String, andere sind subtiler, z. B. das Übergeben ungültiger Argumente an eine API oder sogar komplexe Multithread-Interaktionen.
Abstürze unter Android generieren einen Stacktrace. Das ist ein Snapshot der Reihenfolge der verschachtelten Funktionen, die in Ihrem Programm bis zum Absturz aufgerufen wurden. Sie können sich Absturz-Stacktraces in Android Vitals ansehen.
Stacktrace lesen
Der erste Schritt zur Behebung eines Absturzes besteht darin, die Stelle zu identifizieren, an der er auftritt. Sie können den in den Berichtsdetails verfügbaren Stacktrace verwenden, wenn Sie die Play Console nutzen, oder die Ausgabe des logcat-Tools. Wenn Sie keinen Stacktrace haben, sollten Sie den Absturz lokal reproduzieren. Testen Sie die App dazu entweder manuell oder wenden Sie sich an betroffene Nutzer und reproduzieren Sie den Absturz mit Logcat.
Der folgende Trace zeigt ein Beispiel für einen Absturz in einer App, die mit Jetpack Compose geschrieben wurde:
--------- beginning of crash
AndroidRuntime: FATAL EXCEPTION: main
Process: com.android.developer.crashsample, PID: 3686
java.lang.NullPointerException
at com.android.developer.crashsample.ComposableSingletons$MainActivityKt.lambda$0(MainActivity.kt:27)
at androidx.compose.foundation.ClickableNode.handleUpEvent(Clickable.kt:958)
at androidx.compose.foundation.ClickableNode.onPointerEvent-H0pRuoY(Clickable.kt:895)
at androidx.compose.ui.input.pointer.Node.dispatchMainEventPass(HitPathTracker.kt:446)
at androidx.compose.ui.input.pointer.HitPathTracker.dispatchChanges(HitPathTracker.kt:181)
at androidx.compose.ui.input.pointer.PointerInputEventProcessor.process-BIzXfog(PointerInputEventProcessor.kt:118)
at androidx.compose.ui.platform.AndroidComposeView.dispatchTouchEvent(AndroidComposeView.android.kt:2650)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2969)
at android.app.Activity.dispatchTouchEvent(Activity.java:4683)
at android.os.Looper.loop(Looper.java:398)
at android.app.ActivityThread.main(ActivityThread.java:9569)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:918)
Ein Stacktrace enthält zwei Informationen, die für die Fehlerbehebung bei einem Absturz entscheidend sind:
- Der Typ der ausgelösten Ausnahme.
- Der Abschnitt des Codes, in dem die Ausnahme ausgelöst wird.
Der Typ der ausgelösten Ausnahme gibt in der Regel einen sehr starken Hinweis darauf, was schiefgelaufen ist. Sehen Sie nach, ob es sich um eine IOException, eine OutOfMemoryError oder etwas anderes handelt, und suchen Sie nach der Dokumentation zur Ausnahme-Klasse.
Die Klasse, Methode, Datei und Zeilennummer der Quelldatei, in der die Ausnahme ausgelöst wird, werden in der zweiten Zeile eines Stacktrace angezeigt. Für jede aufgerufene Funktion wird in einer weiteren Zeile die vorherige Aufrufstelle (ein sogenannter Stackframe) angezeigt.
Wenn Sie den Stack durchgehen und den Code untersuchen, finden Sie möglicherweise eine Stelle, an der ein falscher Wert übergeben wird. Wenn Ihr Code nicht im Stacktrace angezeigt wird, haben Sie wahrscheinlich irgendwo einen ungültigen Parameter an einen asynchronen Vorgang übergeben. Oft können Sie herausfinden, was passiert ist, indem Sie jede Zeile des Stacktrace untersuchen, alle verwendeten API-Klassen suchen und prüfen, ob die übergebenen Parameter korrekt sind und ob Sie die API an einer zulässigen Stelle aufgerufen haben.
Stacktraces für Apps mit C- und C++-Code funktionieren weitgehend auf dieselbe Weise.
*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
Build fingerprint: 'google/foo/bar:10/123.456/78910:user/release-keys'
ABI: 'arm64'
Timestamp: 2020-02-16 11:16:31+0100
pid: 8288, tid: 8288, name: com.example.testapp >>> com.example.testapp <<<
uid: 1010332
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0
Cause: null pointer dereference
x0 0000007da81396c0 x1 0000007fc91522d4 x2 0000000000000001 x3 000000000000206e
x4 0000007da8087000 x5 0000007fc9152310 x6 0000007d209c6c68 x7 0000007da8087000
x8 0000000000000000 x9 0000007cba01b660 x10 0000000000430000 x11 0000007d80000000
x12 0000000000000060 x13 0000000023fafc10 x14 0000000000000006 x15 ffffffffffffffff
x16 0000007cba01b618 x17 0000007da44c88c0 x18 0000007da943c000 x19 0000007da8087000
x20 0000000000000000 x21 0000007da8087000 x22 0000007fc9152540 x23 0000007d17982d6b
x24 0000000000000004 x25 0000007da823c020 x26 0000007da80870b0 x27 0000000000000001
x28 0000007fc91522d0 x29 0000007fc91522a0
sp 0000007fc9152290 lr 0000007d22d4e354 pc 0000007cba01b640
backtrace:
#00 pc 0000000000042f89 /data/app/com.example.testapp/lib/arm64/libexample.so (com::example::Crasher::crash() const)
#01 pc 0000000000000640 /data/app/com.example.testapp/lib/arm64/libexample.so (com::example::runCrashThread())
#02 pc 0000000000065a3b /system/lib/libc.so (__pthread_start(void*))
#03 pc 000000000001e4fd /system/lib/libc.so (__start_thread)
Wenn Sie in nativen Stacktraces keine Informationen auf Klassen- und Funktionsebene sehen, müssen Sie möglicherweise eine Datei mit nativen Debugging-Symbolen generieren und in die Google Play Console hochladen. Weitere Informationen finden Sie unter Abgestürzte Stacktraces deobfuskieren. Allgemeine Informationen zu nativen Abstürzen finden Sie unter Native Abstürze diagnostizieren.
Tipps zum Reproduzieren eines Absturzes
Möglicherweise lässt sich das Problem nicht einfach durch Starten eines Emulators oder Verbinden Ihres Geräts mit Ihrem Computer reproduzieren. Entwicklungsumgebungen haben in der Regel mehr Ressourcen wie Bandbreite, Arbeitsspeicher und Speicherplatz. Anhand des Ausnahmetyps können Sie ermitteln, welche Ressource knapp sein könnte, oder eine Korrelation zwischen der Android-Version, dem Gerätetyp oder der Version Ihrer App finden.
Arbeitsspeicherfehler
Wenn Sie ein OutOfMemoryError haben, können Sie einen Emulator mit geringer Speicherkapazität erstellen, um Tests durchzuführen. Abbildung 2 zeigt die AVD Manager-Einstellungen, mit denen Sie den Arbeitsspeicher des Geräts steuern können.

Netzwerkausnahmen
Da Nutzer häufig in und aus der Mobilfunk- oder WLAN-Abdeckung wechseln, sollten Netzwerkfehler in einer Anwendung in der Regel nicht als Fehler, sondern als normale Betriebsbedingungen behandelt werden, die unerwartet auftreten.
Wenn Sie eine Netzwerk-Ausnahme wie UnknownHostException reproduzieren müssen, aktivieren Sie den Flugmodus, während Ihre Anwendung versucht, das Netzwerk zu verwenden.
Eine weitere Möglichkeit besteht darin, die Qualität des Netzwerks im Emulator zu verringern, indem Sie eine Emulation der Netzwerkgeschwindigkeit, eine Netzwerkverzögerung oder beides auswählen. Sie können die Einstellungen Speed (Geschwindigkeit) und Latency (Latenz) im AVD Manager verwenden oder den Emulator mit den Flags -netdelay und -netspeed starten, wie im folgenden Befehlszeilenbeispiel gezeigt:
emulator -avd [your-avd-image] -netdelay 20000 -netspeed gsm
In diesem Beispiel wird eine Verzögerung von 20 Sekunden für alle Netzwerkanfragen und eine Upload- und Downloadgeschwindigkeit von 14,4 Kbit/s festgelegt. Weitere Informationen zu den Befehlszeilenoptionen für den Emulator finden Sie unter Emulator über Befehlszeile starten.
Mit Logcat lesen
Wenn Sie den Absturz reproduzieren können, können Sie ein Tool wie logcat verwenden, um weitere Informationen zu erhalten.
In der logcat-Ausgabe sehen Sie, welche anderen Logmeldungen Sie ausgegeben haben, sowie andere Meldungen aus dem System. Vergessen Sie nicht, alle zusätzlichen Log-Anweisungen zu deaktivieren, die Sie hinzugefügt haben, da das Ausgeben dieser Anweisungen während der Ausführung Ihrer App CPU- und Akkuleistung verbraucht.
Abstürze aufgrund von NullPointerException verhindern
Nullzeiger-Ausnahmen (identifiziert durch den Laufzeitfehlertyp NullPointerException) treten auf, wenn Sie versuchen, auf ein Objekt zuzugreifen, das null ist, in der Regel durch Aufrufen seiner Methoden oder Zugreifen auf seine Elemente. Nullzeiger-Ausnahmen sind die häufigste Ursache für App-Abstürze bei Google Play. Der Zweck von „null“ besteht darin, anzugeben, dass das Objekt fehlt, z. B. weil es noch nicht erstellt oder zugewiesen wurde.
Um NullPointerException zu vermeiden, müssen Sie dafür sorgen, dass die Objektverweise, mit denen Sie arbeiten, nicht null sind, bevor Sie Methoden für sie aufrufen oder versuchen, auf ihre Elemente zuzugreifen. Wenn die Objektreferenz null ist, müssen Sie diesen Fall gut abfangen. Beenden Sie beispielsweise eine Methode, bevor Sie Vorgänge für die Objektreferenz ausführen, und schreiben Sie Informationen in ein Debugging-Log.
Da Sie nicht für jeden Parameter jeder aufgerufenen Methode Null-Prüfungen durchführen möchten, können Sie sich auf die IDE oder den Typ des Objekts verlassen, um die Null-Zulässigkeit zu signalisieren.
Kotlin
In Kotlin ist die Null-Zulässigkeit Teil des Typsystems. Eine Variable muss beispielsweise von Anfang an als „nullable“ oder „non-nullable“ deklariert werden. Nullable-Typen sind mit einem ? gekennzeichnet:
// non-null
var s: String = "Hello"
// null
var s: String? = "Hello"
Nicht nullable Variablen kann kein Nullwert zugewiesen werden. Bei nullable Variablen muss vor der Verwendung als nicht null geprüft werden, ob sie null sind.
Wenn Sie nicht explizit auf „null“ prüfen möchten, können Sie den Operator für sichere Aufrufe ?. verwenden:
val length: Int? = string?.length // length is a nullable int
// if string is null, then length is null
Es empfiehlt sich, den Null-Fall für ein Objekt zu berücksichtigen, das Nullwerte zulässt. Andernfalls kann es sein, dass Ihre App in unerwartete Zustände gerät. Wenn Ihre Anwendung mit NullPointerException nicht mehr abstürzt, wissen Sie nicht, dass diese Fehler vorhanden sind.
Im Folgenden finden Sie einige Möglichkeiten, um auf „null“ zu prüfen:
ifTestsval length = if(string != null) string.length else 0Aufgrund des Smart Cast und der Null-Prüfung weiß der Kotlin-Compiler, dass der Stringwert nicht null ist. Daher können Sie die Referenz direkt verwenden, ohne den Safe Call-Operator zu benötigen.
-
Mit diesem Operator können Sie angeben: „Wenn das Objekt nicht null ist, gib das Objekt zurück. Andernfalls gib etwas anderes zurück.“
val length = string?.length ?: 0
Sie können weiterhin ein NullPointerException in Kotlin erhalten. Das sind die häufigsten Situationen:
- Wenn Sie explizit eine
NullPointerExceptionauslösen. - Wenn Sie den Null-Assertion-Operator
!!verwenden. Dieser Operator konvertiert jeden Wert in einen Nicht-Null-Typ und löstNullPointerExceptionaus, wenn der Wert null ist. - Beim Zugriff auf eine Nullreferenz eines Plattformtyps.
Plattformtypen
Plattformtypen sind Objekterklärungen aus Java. Diese Typen werden speziell behandelt: Null-Prüfungen werden nicht so streng durchgesetzt, sodass die Nicht-Null-Garantie dieselbe wie in Java ist. Wenn Sie auf eine Referenz für einen Plattformtyp zugreifen, werden in Kotlin keine Kompilierzeitfehler erstellt, diese Referenzen können jedoch zu Laufzeitfehlern führen. Hier ein Beispiel aus der Kotlin-Dokumentation:
val list = ArrayList<String>() // non-null (constructor result) list.add("Item")
val size = list.size // non-null (primitive int) val item = list[0] // platform
type inferred (ordinary Java object) item.substring(1) // allowed, may throw an
// exception if item == null
Kotlin verwendet die Typinferenz, wenn einer Kotlin-Variablen ein Plattformwert zugewiesen wird. Sie können aber auch den erwarteten Typ definieren. Am besten stellen Sie den richtigen Null-Zulässigkeit-Zustand einer aus Java stammenden Referenz sicher, indem Sie Null-Zulässigkeit-Annotationen (z. B. @Nullable) in Ihrem Java-Code verwenden. Der Kotlin-Compiler stellt diese Verweise als tatsächliche Nullable- oder Non-Nullable-Typen dar, nicht als Plattformtypen.
Java Jetpack-APIs wurden nach Bedarf mit @Nullable oder @NonNull annotiert. Ein ähnlicher Ansatz wurde im Android 11 SDK verfolgt.
Typen aus diesem SDK, die in Kotlin verwendet werden, werden als korrekte Nullable- oder Non-Nullable-Typen dargestellt.
Das Typsystem von Kotlin reduziert NullPointerException-Abstürze erheblich. So konnte beispielsweise in der Google Home App die Anzahl der Abstürze aufgrund von NullPointerException-Fehlern um 30% gesenkt werden, nachdem die Entwicklung neuer Funktionen auf Kotlin umgestellt wurde.