Aplikacja na Androida ulega awarii przy nieoczekiwanym zamknięciu spowodowanym przez
nieobsługiwany wyjątek lub sygnał. Aplikacja napisana w języku Java lub Kotlin
ulega awarii w przypadku zgłoszenia nieobsłużonego wyjątku, reprezentowanego przez
Throwable
. An
napisanej przy użyciu kodu maszynowego lub awarii języka C++, jeśli istnieje nieobsługiwany
taki jak SIGSEGV
.
Gdy aplikacja ulega awarii, Android przerywa jej proces i wyświetla okno aby powiadomić użytkownika o zatrzymaniu aplikacji, tak jak na ilustracji 1.
Aplikacja nie musi być uruchomiona na pierwszym planie, aby uległa awarii. Dowolna aplikacja nawet komponentów takich jak odbiorniki czy dostawcy treści, działają w tle, może to spowodować awarię aplikacji. Te awarie są co jest często mylące dla użytkowników, ponieważ nie wchodzą w interakcję z aplikacją.
Jeśli w aplikacji występują awarie, możesz skorzystać ze wskazówek na tej stronie, aby: zdiagnozować i rozwiązać problem.
Wykryj problem
Nie zawsze wiesz, że awarie występują u użytkowników gdy używają Twojej aplikacji. Jeśli aplikacja została już opublikowana, możesz użyć Android Vitals pokazuje częstotliwość awarii w Twojej aplikacji.
Android Vitals
Android Vitals może pomóc w monitorowaniu i poprawianiu częstotliwości awarii aplikacji. Android Vitals mierzy kilka częstotliwości awarii:
- Częstotliwość awarii: odsetek aktywnych użytkowników dziennie, którzy nie wystąpiła żadna awaria.
Częstotliwość awarii widocznych dla użytkowników: odsetek aktywnych użytkowników dziennie u których wystąpiła co najmniej 1 awaria podczas aktywnego korzystania z aplikacji (awaria widoczna dla użytkowników). Aplikacja jest uznawana za aktywną. jeśli wyświetla działanie lub wykonuje usługę działającą na pierwszym planie.
Częstotliwość wielokrotnych awarii: odsetek aktywnych użytkowników dziennie, którzy wystąpiły co najmniej 2 awarie.
Aktywni użytkownicy dziennie to unikalny użytkownik, który korzysta z Twojej aplikacji. w ciągu jednego dnia na 1 urządzeniu, potencjalnie w trakcie kilku sesji. Jeśli użytkownik korzysta z aplikacji na więcej niż 1 urządzeniu w ciągu 1 dnia, każde urządzenie zwiększy liczbę aktywnych użytkowników w danym dniu. Jeśli z tego samego urządzenia korzysta wielu użytkowników w ciągu 1 dnia, jest liczony jako 1 aktywny użytkownik.
Częstotliwość awarii widocznych dla użytkowników to podstawowe znaczenie, co oznacza, że wpływa na wykrywalność Twojej aplikacji w Google Play. Jest to ważne, ponieważ powoduje awarie mają miejsce zawsze wtedy, gdy użytkownik korzysta z aplikacji, nie jest zakłócona.
W Google Play obowiązują 2 progi niewłaściwego działania dotyczące tych danych:
- Ogólny próg niewłaściwego działania: co najmniej 1,09% aktywnych użytkowników dziennie. może nastąpić awaria widoczna dla użytkowników na wszystkich modelach urządzeń.
- Próg niewłaściwego działania na urządzenie: co najmniej 8% aktywnych użytkowników dziennie. wystąpiła awaria widoczna dla użytkowników na 1 modelu urządzenia.
Jeśli aplikacja przekracza ogólny próg niewłaściwego działania, prawdopodobnie trudniejsze do znalezienia na wszystkich urządzeniach. Jeśli aplikacja przekracza niewłaściwe działanie na konkretnych urządzeniach na pewnych urządzeniach może być trudniej znaleźć na tych urządzeniach, i na stronie z informacjami o aplikacji może pojawić się ostrzeżenie.
Android Vitals może Cię ostrzec za pomocą Konsola Play gdy występują liczne awarie aplikacji.
Informacje o tym, jak Google Play zbiera dane Android Vitals, znajdziesz w Konsola Play dokumentacji.
Zdiagnozuj awarie
Gdy ustalisz, że aplikacja zgłasza awarie, następnym krokiem jest diagnoza. Naprawianie awarii może być trudne. Jeśli jednak możesz ustalić główną przyczynę po awarii, prawdopodobnie znajdziesz rozwiązanie problemu.
Awarie aplikacji mogą wynikać z wielu sytuacji. Niektóre przyczyny: oczywiste, takie jak sprawdzanie wartości null lub pustego ciągu, ale inne są bardziej subtelne, np. przez przekazywanie nieprawidłowych argumentów do interfejsu API, czy nawet złożonych wielowątkowości interakcje.
Awarie na Androidzie powodują utworzenie zrzutu stosu, które jest zrzutem sekwencji zagnieżdżone funkcje wywoływane w programie do momentu awarii. Dostępne opcje wyświetl zrzuty stosu awarii w Android Vitals.
Jak odczytywać zrzut stosu
Pierwszym krokiem do naprawienia awarii jest określenie miejsca, w którym do niej doszło. Dostępne opcje jeśli korzystasz z Google Play, użyj zrzutu stosu dostępnego w szczegółach raportu. Konsola lub dane wyjściowe narzędzia logcat. Jeśli nie dostępny jest zrzut stosu, musisz odtworzyć lokalnie błąd, ręcznie testując aplikację lub kontaktując się z użytkownikami, których dotyczy problem, w celu jego odtworzenia za pomocą narzędzia logcat.
Poniższy ślad pokazuje przykład awarii w aplikacji napisanej w języku Java język programowania:
--------- beginning of crash
AndroidRuntime: FATAL EXCEPTION: main
Process: com.android.developer.crashsample, PID: 3686
java.lang.NullPointerException: crash sample
at com.android.developer.crashsample.MainActivity$1.onClick(MainActivity.java:27)
at android.view.View.performClick(View.java:6134)
at android.view.View$PerformClick.run(View.java:23965)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:156)
at android.app.ActivityThread.main(ActivityThread.java:6440)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:746)
--------- beginning of system
Zrzut stosu pokazuje 2 rodzaje informacji, które są kluczowe dla debugowania awaria:
- Typ zgłoszonego wyjątku.
- Sekcja kodu, w której zgłaszany jest wyjątek.
Rodzaj zgłoszenia wyjątku jest zwykle bardzo mocną wskazówką, co powinno się stać
źle. Sprawdź, czy
IOException
,
OutOfMemoryError
,
lub innego elementu, a potem znajdź dokumentację klasy wyjątku.
Klasa, metoda, plik i numer wiersza pliku źródłowego, których dotyczy wyjątek jest widoczny w drugim wierszu zrzutu stosu. Dla każdej funkcji, która , kolejny wiersz pokazuje poprzedni wiersz wywołania (nazywany ramką stosu). Przechodząc na wyższy poziom i przeglądając kod, możesz znaleźć miejsce, które przekazują nieprawidłową wartość. Jeśli Twojego kodu nie ma w zrzucie stosu, prawdopodobnie gdzieś został przekazany nieprawidłowy parametr do . Często można się dowiedzieć, co się stało, analizując każdy wiersz śledzenie stosu, znajdowanie używanych klas interfejsów API i potwierdzanie, że przesłane parametry były prawidłowe i zostały wywołane z miejsca, jest dozwolona.
Zrzuty stosu aplikacji z kodami w C i C++ działają mniej więcej tak samo.
*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
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)
Jeśli w natywnych zrzutach stosu nie widzisz informacji na poziomie klasy i funkcji, może być konieczne wygeneruj plik symboli do debugowania kodu natywnego i prześlij ją do Konsoli Google Play. Więcej informacji: Usuwanie zaciemnienia kodu w zrzutach stosu awarii. Ogólne informacje o awariach natywnych znajdziesz w artykule Diagnozuj awarie reklam natywnych
Wskazówki dotyczące odtwarzania wypadku
Może się zdarzyć, że nie uda się w pełni odtworzyć problemu, uruchamiając za pomocą emulatora lub podłączania urządzenia do komputera. Środowiska programistyczne mają zwykle więcej zasobów, takich jak przepustowość, pamięć i miejsce na dane. Użyj typu wyjątku w celu określenia, jakiego rodzaju zasobów może brakować, określić związek między wersją Androida, typem urządzenia a wersji.
Błędy pamięci
Jeśli posiadasz
OutOfMemoryError
,
możesz utworzyć emulator o małej pojemności pamięci, na którym przeprowadzisz testy. Ilustracja
2 pokazuje ustawienia menedżera AVD, w których można kontrolować ilość pamięci
urządzenia.
Wyjątki sieci
Użytkownicy często przemieszczają się poza zasięg sieci komórkowej lub Wi-Fi, wyjątki sieci aplikacji zwykle nie powinny być traktowane jako błędy, ale w zwykłych warunkach działania, które pojawiają się nieoczekiwanie.
Jeśli chcesz odtworzyć wyjątek sieci, taki jak
UnknownHostException
,
a następnie włącz tryb samolotowy, a aplikacja spróbuje użyć
Inną możliwością jest zmniejszenie jakości sieci w emulatorze przez
wybór emulacji szybkości sieci lub opóźnienia sieci. Za pomocą
Ustawienia Szybkość i Czas oczekiwania w menedżerze AVD lub możesz uruchomić emulator
z flagami -netdelay
i -netspeed
, jak pokazano poniżej
przykład wiersza poleceń:
emulator -avd [your-avd-image] -netdelay 20000 -netspeed gsm
W tym przykładzie ustawiane jest 20-sekundowe opóźnienie w przypadku wszystkich żądań sieciowych i przesyłania a szybkość pobierania 14,4 kb/s. Więcej informacji o opcjach wiersza poleceń emulatora, zobacz Uruchom emulator z wiersza poleceń.
Czytanie z logcat
Gdy już będziesz mieć sposoby na odtworzenie awarii, możesz skorzystać z narzędzia takiego jak
logcat
, aby uzyskać więcej informacji.
W danych wyjściowych logcat zobaczysz, jakie inne komunikaty dziennika zostały wydrukowane,
oraz inne dane w systemie. Nie zapomnij wyłączyć dodatkowych funkcji
Log
– oświadczenia,
zostały dodane, bo ich drukowanie zużywa procesor i baterię, gdy aplikacja jest
w domu.
Zapobiegaj awariom spowodowanym przez wyjątki wskaźnika null
Wyjątki wskaźnika o wartości null (określane na podstawie typu błędu środowiska wykonawczego)
NullPointerException
) występują, gdy próbujesz uzyskać dostęp do obiektu, który
null, zazwyczaj przez wywoływanie jego metod lub uzyskiwanie dostępu do elementów. Wskaźnik pusty
to najczęstsza przyczyna awarii aplikacji w Google Play. Cel
null oznacza brak obiektu – na przykład
utworzone lub przypisane. Aby uniknąć wyjątków od wskaźnika null, musisz upewnić się,
do których odwołuje się Twój obiekt, nie mają wartości null przed wywołaniem
lub próby uzyskania do nich dostępu. Jeśli odwołanie do obiektu to
null, należy traktować to dobrze (np. wyjście z metody przed wykonaniem
wykonywać wszystkie operacje na odwołaniach do obiektu i zapisywać informacje w dzienniku debugowania).
Ponieważ nie chcesz sprawdzać wartości null dla każdego parametru i każdej metody możesz polegać na IDE lub typie obiektu dopuszczalna wartość null.
Język programowania Java
Sekcje poniżej dotyczą języka programowania Java.
Ostrzeżenia dotyczące czasu kompilacji
Dodaj adnotacje do metod i zwracają wartości z argumentem
@Nullable
i
@NonNull
, aby otrzymać czas kompilacji
z IDE. Te ostrzeżenia informują, że należy spodziewać się obiektu z możliwością wartości null:
Te kontrole o wartości null dotyczą obiektów, o których wiesz, że mogą mieć wartość null. Wyjątek
Obiekt @NonNull
wskazuje na błąd w kodzie, który musi zostać
oraz je wyeliminować.
Błędy podczas kompilacji
Ponieważ wartość null powinna być istotna, możesz umieścić ją w używanych przez Ciebie typach
dzięki czemu istnieje kontrola czasu kompilacji na wartość null. Jeśli wiesz, że obiekt może być
i że możliwość null powinna być obsługiwana, możesz ją ująć w obiekt
Optional
Należy zawsze preferować typy, które przekazują wartość null.
Kotlin
W Kotlin
dopuszczalność wartości null
jest częścią systemu typów. Na przykład zmienna musi być zadeklarowana z
może mieć wartość null lub nie. Typy, które mogą być wartości null, są oznaczone za pomocą ?
:
// non-null
var s: String = "Hello"
// null
var s: String? = "Hello"
Zmiennym niedopuszczającym wartości null nie można przypisać wartości null ani zmiennych dopuszczonych do wartości null trzeba sprawdzić pod kątem możliwości null, zanim będzie używany jako inny niż null.
Jeśli nie chcesz wprost sprawdzać wartości null, możesz użyć funkcji bezpiecznego wywołania ?.
:
val length: Int? = string?.length // length is a nullable int
// if string is null, then length is null
Zalecaną metodą jest rozwiązanie problemu z wartością null dla obiektu z możliwością wartości null,
lub może pojawić się
nieoczekiwany stan. Jeśli aplikacja nie ulega awarii
z usługą NullPointerException
, nie dowiesz się o ich istnieniu.
Oto kilka sposobów na sprawdzenie tej wartości:
if
kontrolival length = if(string != null) string.length else 0
Dzięki funkcji smartcast i kontroli zerowej kompilator Kotlin wie, że ma wartość różną od null, więc pozwala na bezpośrednie użycie odwołania, bez konieczności korzystania z operatora bezpiecznego połączenia.
-
Ten operator umożliwia zadeklarowanie, że „jeśli obiekt nie ma wartości null, zwróć obiektu; w przeciwnym razie zwróć coś innego”.
val length = string?.length ?: 0
Nadal możesz uzyskać NullPointerException
w Kotlin. Oto najczęstsze z nich
typowe sytuacje:
- Kiedy wyraźnie rzucasz
NullPointerException
. - Jeśli używasz tagu
operator asercji null
!!
. Ten operator konwertuje dowolną wartość na typ niepusty, powodując rzutowanieNullPointerException
, jeśli wartość jest pusta. - Gdy uzyskujesz dostęp do pustego odwołania danego typu platformy.
Typy platform
Typy platform to deklaracje obiektów pochodzące z Javy. Trzeba nad nimi zapracować. Testy o wartości null nie są tak egzekwowane, więc gwarancja niepusta jest taka sama jak w Java. Gdy uzyskujesz dostęp do odwołania do typu platformy, Kotlin nie tworzy kompilacji ale mogą one powodować błędy podczas działania. Zobacz poniższe informacje przykład z dokumentacji Kotlin:
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 opiera się na wnioskowaniu typu, gdy wartość platformy jest przypisana do Kotlina
możesz też określić oczekiwany typ. Najlepszy sposób, by zapewnić
prawidłowy stan dopuszczalności null w pliku referencyjnym pochodzącym z Javy to użycie dopuszczalności null
adnotacji (np. @Nullable
) w kodzie Java. Kompilator Kotlin
będą reprezentować te odwołania jako rzeczywiste typy z możliwością wartości null lub niedopuszczające wartości null, a nie jako
typów platform.
Do interfejsów API Java Jetpack w razie potrzeby dodano adnotacje @Nullable
lub @NonNull
.
Podobne podejście przyjęto
Pakiet SDK do Androida 11.
Typy pochodzące z tego pakietu SDK używane w kotlinie będą reprezentowane jako
poprawnych lub niedopuszczających wartości null lub niedopuszczających wartości null.
Ze względu na system typów w kotlinie zauważyliśmy, że w przypadku aplikacji nastąpił znaczny spadek
NullPointerException
awarii. Na przykład aplikacja Google Home uzyskała 30%
ograniczenie liczby awarii spowodowanych przez wyjątki od wskaźnika zerowego w roku, w którym
przeprowadziliśmy migrację nowych funkcji do Kotlin.