Un'app per Android ha un arresto anomalo ogni volta che si verifica un'uscita imprevista causata da un
un'eccezione o un indicatore non gestito. Un'app scritta utilizzando Java o Kotlin
si arresta in modo anomalo se genera un'eccezione non gestita, rappresentata dal
Throwable
. Un
l'app scritta utilizzando un codice macchina o C++ si arresta in modo anomalo in caso di una
come SIGSEGV
, durante la sua esecuzione.
In caso di arresto anomalo di un'app, Android termina il processo dell'app e mostra una finestra di dialogo per comunicare all'utente che l'app è stata interrotta, come mostrato nella figura 1.
Non è necessario che un'app sia in esecuzione in primo piano per arrestarsi in modo anomalo. Qualsiasi app anche componenti come broadcast receiver o fornitori di contenuti che sono in esecuzione in background, possono causare l'arresto anomalo di un'app. Questi arresti anomali sono spesso confondono gli utenti perché non hanno interagito attivamente con l'app.
In caso di arresti anomali dell'app, puoi utilizzare le indicazioni fornite in questa pagina per diagnosticare e risolvere il problema.
Rilevare il problema
Potresti non sapere sempre che i tuoi utenti subiscono arresti anomali quando utilizzano la tua app. Se hai già pubblicato la tua app, puoi utilizzare Android vitals per controllare la percentuale di arresti anomali della tua app.
Android vitals
Android vitals può aiutarti a monitorare e migliorare la percentuale di arresti anomali dell'app. Android vitals misura diverse percentuali di arresti anomali:
- Percentuale di arresti anomali: la percentuale di utenti attivi giornalieri che riscontrato alcun tipo di arresto anomalo.
Percentuale di arresti anomali percepiti dagli utenti: la percentuale di utenti attivi giornalieri. che hanno riscontrato almeno un arresto anomalo mentre stavano utilizzando attivamente la tua app (un arresto anomalo percepito dall'utente). Un'app è considerata in uso attivo se mostra attività o esegue servizio in primo piano.
Percentuale di arresti anomali multipli: la percentuale di utenti attivi giornalieri che hanno subito almeno due arresti anomali.
Un utente attivo giornaliero è un utente unico che utilizza la tua app. in un solo giorno su un solo dispositivo, potenzialmente in più sessioni. Se un utente utilizza la tua app su più dispositivi in un solo giorno: ogni dispositivo contribuirà al numero di utenti attivi per quel giorno. Se più utenti usano lo stesso dispositivo in un solo giorno, questo viene conteggiato come un utente attivo.
La percentuale di arresti anomali percepiti dagli utenti è una metrica fondamentale, ovvero influisce sulle la rilevabilità della tua app su Google Play. È importante perché gli arresti anomali I conteggi si verificano sempre quando l'utente interagisce con l'app, generando il e un'interruzione del servizio.
Google Play ha definito due soglie relative alle prestazioni scadenti per questa metrica:
- Soglia relativa alle prestazioni scadenti generali: almeno l'1,09% degli utenti attivi giornalieri. riscontrano un arresto anomalo percepito dall'utente su tutti i modelli di dispositivi.
- Soglia relativa alle prestazioni scadenti per dispositivo: almeno l'8% degli utenti attivi giornalieri. rilevare un arresto anomalo percepito dall'utente per un singolo modello di dispositivo.
Se la tua app supera la soglia relativa alle prestazioni scadenti generali, è probabile che è meno rilevabile su tutti i dispositivi. Se la tua app supera le prestazioni scadenti del dispositivo soglia su alcuni dispositivi, è probabile che sia meno rilevabile su tali dispositivi, e potrebbe essere mostrato un avviso nella tua scheda dello Store.
Android vitals può avvisarti tramite Play Console quando la tua app presenta arresti anomali eccessivi.
Per informazioni sulla modalità di raccolta dei dati Android vitals in Google Play, consulta le Play Console documentazione.
Diagnostica gli arresti anomali
Una volta identificato che la tua app segnala arresti anomali, il il passaggio successivo è la diagnosi. La risoluzione degli arresti anomali può essere difficile. Tuttavia, se riesci a identificare la causa principale l'arresto anomalo, molto probabilmente potrai trovare una soluzione.
Esistono molte situazioni che possono causare un arresto anomalo nella tua app. Alcuni motivi sono ovvi, come il controllo di un valore nullo o di una stringa vuota, ma altri sono più sottile, come il passaggio di argomenti non validi a un'API o anche complessi e interazioni.
Gli arresti anomali su Android generano un'analisi dello stack, ovvero un'istantanea della sequenza funzioni nidificate chiamate nel programma fino al momento in cui si è verificato l'arresto anomalo. Puoi per visualizzare le analisi dello stack in caso di arresto anomalo Android vitals.
Come leggere un'analisi dello stack
Il primo passaggio per correggere un arresto anomalo è identificare il luogo in cui si verifica. Puoi Usa l'analisi dello stack disponibile nei dettagli del report se usi Google Play Console o l'output dello strumento logcat. Se non è disponibile un'analisi dello stack, devi riprodurre in locale l'arresto anomalo, testando manualmente l'app o contattando gli utenti interessati; e riprodurlo utilizzando logcat.
La traccia seguente mostra un esempio di arresto anomalo su un'app scritta utilizzando il linguaggio Java linguaggio di programmazione:
--------- 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
Un'analisi dello stack mostra due informazioni fondamentali per il debug di un arresto anomalo:
- Il tipo di eccezione generato.
- La sezione del codice in cui viene generata l'eccezione.
Il tipo di eccezione presentato è in genere un'indicazione molto utile in merito a ciò che è
sbagliato. Verifica se si tratta di una
IOException
, un
OutOfMemoryError
,
o altro, e cerca la documentazione relativa alla classe di eccezione.
La classe, il metodo, il file e il numero di riga del file di origine in cui l'eccezione viene restituito sulla seconda riga di un'analisi dello stack. Per ogni funzione un'altra riga mostra il precedente sito di chiamata (chiamato stack frame). Camminando nella pila ed esaminando il codice, potresti trovare un luogo passare un valore errato. Se il codice non compare nell'analisi dello stack, è probabile che tu abbia passato un parametro non valido in un operativa. Spesso puoi capire cosa è successo esaminando ogni riga della dell'analisi dello stack, individuando le classi API che hai utilizzato e verificando che i parametri trasmessi erano corretti e che l'avevi chiamato da una posizione consentito.
Le analisi dello stack per le app con codice C e C++ funzionano più o meno allo stesso modo.
*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
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)
Se non vedi informazioni a livello di classe e funzione nelle analisi dello stack native, potresti dover generare un file di simboli di debug nativo e caricarlo su Google Play Console. Per ulteriori informazioni, vedi Deoffuscare le analisi dello stack in caso di arresto anomalo. Per informazioni generali sugli arresti anomali nativi, consulta Diagnosticare gli arresti anomali nativi.
Suggerimenti per riprodurre un arresto anomalo
È possibile che non sia possibile riprodurre il problema semplicemente avviando emulatore o collegando il dispositivo al computer. Ambienti di sviluppo tendono ad avere più risorse, come larghezza di banda, memoria e spazio di archiviazione. Utilizza la un tipo di eccezione per determinare quale potrebbe essere la risorsa che è scarsa o trovare una correlazione tra la versione di Android, il tipo di dispositivo o la versione completamente gestita.
Errori di memoria
Se disponi di un
OutOfMemoryError
,
puoi creare un emulatore con poca capacità di memoria da testare. Figura
2 mostra le impostazioni di Gestione AVD con cui è possibile controllare la quantità di memoria
del dispositivo.
Eccezioni di networking
Dato che gli utenti entrano ed escono spesso dalla copertura di rete mobile o Wi-Fi, in una le eccezioni alla rete delle applicazioni in genere non devono essere trattate come errori, ma e non come normali condizioni operative che si verificano inaspettatamente.
Se devi riprodurre un'eccezione di rete, come un
UnknownHostException
,
quindi prova ad attivare la modalità aereo mentre l'applicazione tenta di utilizzare
in ogni rete.
Un'altra opzione è ridurre la qualità della rete nell'emulatore
scegliere un'emulazione della velocità di rete e/o un ritardo di rete. Puoi utilizzare lo
Impostazioni Velocità e Latenza su Gestione AVD oppure avvia l'emulatore
con i flag -netdelay
e -netspeed
, come mostrato di seguito
esempio della riga di comando:
emulator -avd [your-avd-image] -netdelay 20000 -netspeed gsm
In questo esempio viene impostato un ritardo di 20 secondi su tutte le richieste di rete e su un caricamento e velocità di download di 14,4 Kbps. Per ulteriori informazioni sulle opzioni della riga di comando per l'emulatore, vedi Avvia l'emulatore dalla riga di comando.
Lettura con logcat
Una volta ottenuti i passaggi per riprodurre l'arresto anomalo, puoi utilizzare uno strumento come
logcat
per avere ulteriori informazioni.
L'output di logcat mostrerà quali altri messaggi di log hai stampato, insieme
con gli altri dal sistema. Non dimenticare di disattivare le funzionalità aggiuntive
Log
dichiarazioni che hai
che hai aggiunto perché la stampa comporta sprechi di CPU e batteria mentre l'app
in esecuzione.
Impedisci gli arresti anomali causati da eccezioni di puntatori nulli
Eccezioni di puntatore nullo (identificate dal tipo di errore di runtime
NullPointerException
) si verificano quando tenti di accedere a un oggetto che
nullo, in genere richiamando i relativi metodi o accedendo ai relativi membri. Puntatore null
le eccezioni sono la causa principale di arresti anomali dell'app su Google Play. Lo scopo di
null indica che l'oggetto è mancante, ad esempio, non è stato
creati o assegnati. Per evitare eccezioni relative a puntatori nulli, devi assicurarti
che l'oggetto con cui stai lavorando siano non null prima di chiamare
metodi o tentare di accedere ai membri. Se il riferimento all'oggetto è
null, gestisci bene questo caso (ad esempio, l'uscita da un metodo prima di eseguire
operazioni sul riferimento dell'oggetto e sulla scrittura di informazioni in un log di debug).
Non vuoi avere controlli nulli per ogni parametro di ogni metodo. puoi fare affidamento sull'IDE o sul tipo di oggetto per indicare con supporto di valori null.
Linguaggio di programmazione Java
Le seguenti sezioni riguardano il linguaggio di programmazione Java.
Avvisi relativi al tempo di compilazione
Annota i tuoi metodi e restituiscono valori con
@Nullable
e
@NonNull
per ricevere il tempo di compilazione
avvisi dall'IDE. Questi avvisi ti chiedono di aspettarsi un oggetto con valori nulli:
Questi controlli null riguardano gli oggetti che possono essere nulli. Un'eccezione a un
L'oggetto @NonNull
indica un errore nel codice che deve essere
gestiti.
Errori relativi al tempo di compilazione
Poiché il valore null deve essere significativo, puoi incorporarlo nei tipi che utilizzi
esiste un controllo in fase di compilazione per il valore null. Se sai che un oggetto può essere
nullo e che sia necessario gestire il valore null, potresti includerlo in un oggetto come
Optional
Dovresti sempre preferire tipi che comunicano valori nulli.
Kotlin
In Kotlin,
nullabilità
fa parte del sistema dei tipi. Ad esempio, devi dichiarare una variabile
l'inizio come nullable o non-nullable. I tipi null sono contrassegnati con un ?
:
// non-null
var s: String = "Hello"
// null
var s: String? = "Hello"
Non è possibile assegnare alle variabili senza valori null un valore e variabili con valori nulli. e devono essere verificati per verificare la presenza di valori nulli prima di poter essere utilizzati come non null.
Se non vuoi verificare in modo esplicito null, puoi utilizzare la chiamata sicura ?.
operatore:
val length: Int? = string?.length // length is a nullable int
// if string is null, then length is null
Come best practice, assicurati di gestire le maiuscole e le minuscole per un oggetto nullo.
o che l'app possa avere stati imprevisti. Se l'applicazione non si arresta in modo anomalo
con NullPointerException
, non saprai più che questi errori esistono.
Di seguito sono riportati alcuni modi per verificare la presenza di valori nulli:
if
controllival length = if(string != null) string.length else 0
Grazie allo smart-cast e al controllo del valore null, il compilatore Kotlin sa che il valore della stringa è diverso da null, quindi consente di utilizzare direttamente il riferimento, senza dover ricorrere all'operatore della chiamata sicura.
-
Questo operatore consente di indicare "Se l'oggetto non è null, restituisce il object; altrimenti, restituisci qualcos'altro".
val length = string?.length ?: 0
Puoi ancora ricevere NullPointerException
in Kotlin. Di seguito sono riportate le
di situazioni comuni:
- Quando lanci esplicitamente un
NullPointerException
. - Quando utilizzi
operatore
!!
dell'asserzione nulla. Questo operatore converte qualsiasi valore in un tipo non nullo, generandoNullPointerException
se il valore è null. - Quando si accede a un riferimento nullo di un tipo di piattaforma.
Tipi di piattaforma
I tipi di piattaforma sono dichiarazioni di oggetti provenienti da Java. Questi tipi vengono trattati in modo speciale. controlli null non sono applicati, quindi la garanzia non null è la stessa di Java. Quando accedi a un riferimento a un tipo di piattaforma, Kotlin non crea ma questi riferimenti possono causare errori di runtime. Consulta quanto segue: esempio tratto dalla documentazione di 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 si basa sull'inferenza del tipo quando un valore di piattaforma viene assegnato a un
oppure puoi definire il tipo di previsione. Il modo migliore per garantire che
Per correggere lo stato dei valori null di un riferimento proveniente da Java è necessario utilizzare i valori NULL
(ad esempio, @Nullable
) nel codice Java. Il compilatore Kotlin
rappresenterà questi riferimenti come tipi effettivi con valori null o non null, non come
tipi di piattaforma.
Le API Java Jetpack sono state annotate con @Nullable
o @NonNull
a seconda delle esigenze.
ed è stato adottato un approccio simile
SDK per Android 11.
I tipi provenienti da questo SDK e utilizzati in Kotlin verranno rappresentati come
i tipi corretti con valori null o non null.
Grazie al sistema dei tipi di Kotlin, abbiamo riscontrato una riduzione significativa delle app nelle
NullPointerException
arresti anomali. Ad esempio, l'app Google Home ha registrato il 30%
di riduzione degli arresti anomali causati da eccezioni di puntatori nulli durante l'anno in cui
ha migrato lo sviluppo di nuove funzionalità in Kotlin.