Questo codelab fa parte del corso Android Kotlin Fundamentals. Per ottenere il massimo valore da questo corso, ti consigliamo di seguire le codelab in sequenza. Tutti i codelab del corso sono elencati nella pagina di destinazione dei codelab Android Kotlin Fundamentals.
Introduzione
Nell'ultimo codelab hai imparato a conoscere i cicli di vita di Activity e Fragment e hai esplorato i metodi chiamati quando lo stato del ciclo di vita cambia in attività e fragment. In questo codelab, esplorerai il ciclo di vita dell'attività in modo più dettagliato. Scopri anche la libreria del ciclo di vita di Android Jetpack, che può aiutarti a gestire gli eventi del ciclo di vita con un codice meglio organizzato e più facile da gestire.
Cosa devi già sapere
- Che cos'è un'attività e come crearne una nella tua app.
- Le nozioni di base dei cicli di vita di
ActivityeFragmente i callback richiamati quando un'attività passa da uno stato all'altro. - Come eseguire l'override dei metodi di callback del ciclo di vita
onCreate()eonStop()per eseguire operazioni in momenti diversi del ciclo di vita dell'attività o del frammento.
Cosa imparerai a fare
- Come configurare, avviare e arrestare parti dell'app nei callback del ciclo di vita.
- Come utilizzare la libreria del ciclo di vita di Android per creare un osservatore del ciclo di vita e semplificare la gestione del ciclo di vita di attività e frammenti.
- In che modo gli arresti anomali di Android influiscono sui dati della tua app e come salvare e ripristinare automaticamente questi dati quando Android chiude l'app.
- In che modo la rotazione del dispositivo e altre modifiche alla configurazione creano modifiche agli stati del ciclo di vita e influiscono sullo stato dell'app.
Attività previste
- Modifica l'app DessertClicker in modo da includere una funzione timer e avvia e interrompi il timer in vari momenti del ciclo di vita dell'attività.
- Modifica l'app per utilizzare la libreria del ciclo di vita Android e converti la classe
DessertTimerin un osservatore del ciclo di vita. - Configura e utilizza Android Debug Bridge (
adb) per simulare l'arresto del processo della tua app e i callback del ciclo di vita che si verificano in quel momento. - Implementa il metodo
onSaveInstanceState()per conservare i dati delle app che potrebbero essere persi se l'app viene chiusa inaspettatamente. Aggiungi il codice per ripristinare i dati quando l'app viene riavviata.
In questo codelab, espandi l'app DessertClicker del codelab precedente. Aggiungi un timer in background, poi converti l'app per utilizzare la libreria del ciclo di vita di Android.

Nel codelab precedente hai imparato a osservare i cicli di vita di attività e fragment eseguendo l'override di vari callback del ciclo di vita e registrando quando il sistema richiama questi callback. In questa attività, esplorerai un esempio più complesso di gestione delle attività del ciclo di vita nell'app DessertClicker. Utilizzerai un timer che stampa un'istruzione di log ogni secondo, con il conteggio del numero di secondi di esecuzione.
Passaggio 1: configura DessertTimer
- Apri l'app DessertClicker dell'ultimo codelab. Se non hai l'app, puoi scaricare DessertClickerLogs qui.
- Nella visualizzazione Progetto, espandi java > com.example.android.dessertclicker e apri
DessertTimer.kt. Nota che al momento tutto il codice è commentato, quindi non viene eseguito come parte dell'app. - Seleziona tutto il codice nella finestra dell'editor. Seleziona Codice > Commento con commento di riga o premi
Control+/(Command+/su Mac). Questo comando rimuove il commento da tutto il codice nel file. Android Studio potrebbe mostrare errori di riferimento non risolti finché non ricompili l'app. - Tieni presente che la classe
DessertTimerincludestartTimer()estopTimer(), che avviano e arrestano il timer. QuandostartTimer()è in esecuzione, il timer stampa un messaggio di log ogni secondo, con il conteggio totale dei secondi di esecuzione. Il metodostopTimer(), a sua volta, interrompe il timer e le istruzioni di log.
- Apri
MainActivity.kt. Nella parte superiore della classe, appena sotto la variabiledessertsSold, aggiungi una variabile per il timer:
private lateinit var dessertTimer : DessertTimer;- Scorri verso il basso fino a
onCreate()e crea un nuovo oggettoDessertTimersubito dopo la chiamata asetOnClickListener():
dessertTimer = DessertTimer()
Ora che hai un oggetto timer per il dolce, valuta dove dovresti avviare e interrompere il timer per farlo funzionare solo quando l'attività è sullo schermo. Ne esaminerai alcune nei passaggi successivi.
Passaggio 2: avvia e interrompi il timer
Il metodo onStart() viene chiamato appena prima che l'attività diventi visibile. Il metodo onStop() viene chiamato dopo che l'attività non è più visibile. Questi callback sembrano buoni candidati per l'avvio e l'interruzione del timer.
- Nella classe
MainActivity, avvia il timer nel callbackonStart():
override fun onStart() {
super.onStart()
dessertTimer.startTimer()
Timber.i("onStart called")
}- Interrompi il timer tra
onStop():
override fun onStop() {
super.onStop()
dessertTimer.stopTimer()
Timber.i("onStop Called")
}- Compila ed esegui l'app. In Android Studio, fai clic sul riquadro Logcat. Nella casella di ricerca Logcat, inserisci
dessertclicker, che filtrerà in base alle classiMainActivityeDessertTimer. Tieni presente che una volta avviata l'app, il timer inizia a funzionare immediatamente.
- Fai clic sul pulsante Indietro e noterai che il timer si ferma di nuovo. Il timer si interrompe perché sia l'attività che il timer che controlla sono stati eliminati.
- Usa la schermata Recenti per tornare all'app. In Logcat, noterai che il timer riparte da 0.
- Fai clic sul pulsante Condividi. In Logcat noterai che il timer è ancora in esecuzione.

- Fai clic sul pulsante Home. Nota in Logcat che il timer smette di funzionare.
- Usa la schermata Recenti per tornare all'app. Nota in Logcat che il timer riprende da dove era stato interrotto.
- In
MainActivity, nel metodoonStop(), commenta la chiamata astopTimer(). Il commentostopTimer()mostra il caso in cui avvii un'operazione inonStart(), ma dimentichi di interromperla di nuovo inonStop(). - Compila ed esegui l'app, quindi fai clic sul pulsante Home dopo l'avvio del timer. Anche se l'app è in background, il timer è in esecuzione e utilizza continuamente le risorse di sistema. Se il timer continua a funzionare, si verifica una perdita di memoria per l'app e probabilmente non è il comportamento che vuoi.
Il modello generale prevede che quando configuri o avvii qualcosa in un callback, lo interrompi o lo rimuovi nel callback corrispondente. In questo modo, eviti di avere qualcosa in esecuzione quando non è più necessario.
- Rimuovi il commento dalla riga in
onStop()in cui interrompi il timer. - Taglia e incolla la chiamata
startTimer()daonStart()aonCreate(). Questa modifica mostra il caso in cui inizializzi e avvii una risorsa inonCreate(), anziché utilizzareonCreate()per inizializzarla eonStart()per avviarla. - Compila ed esegui l'app. Nota che il timer inizia a funzionare, come previsto.
- Fai clic su Home per arrestare l'app. Il timer si interrompe, come previsto.
- Utilizza la schermata Recenti per tornare all'app. Tieni presente che il timer non viene riavviato in questo caso, perché
onCreate()viene chiamato solo all'avvio dell'app, non quando un'app torna in primo piano.
Punti chiave da ricordare:
- Quando configuri una risorsa in un callback del ciclo di vita, smontala.
- Esegui la configurazione e l'eliminazione nei metodi corrispondenti.
- Se configuri qualcosa in
onStart(), interrompilo o rimuovilo di nuovo inonStop().
Nell'app DessertClicker è abbastanza facile capire che se hai avviato il timer in onStart(), devi arrestarlo in onStop(). C'è un solo timer, quindi non è difficile ricordarsi come interromperlo.
In un'app per Android più complessa, potresti configurare molte cose in onStart() o onCreate(), per poi smantellarle in onStop() o onDestroy(). Ad esempio, potresti avere animazioni, musica, sensori o timer che devi configurare e smontare, avviare e interrompere. Se ne dimentichi una, si verificano bug e problemi.
La libreria del ciclo di vita, che fa parte di Android Jetpack, semplifica questa attività. La libreria è particolarmente utile nei casi in cui devi monitorare molte parti mobili, alcune delle quali si trovano in stati del ciclo di vita diversi. La libreria inverte il funzionamento dei cicli di vita: di solito l'attività o il fragment indica a un componente (ad esempio DessertTimer) cosa fare quando si verifica un callback del ciclo di vita. Tuttavia, quando utilizzi la libreria del ciclo di vita, il componente stesso monitora le modifiche del ciclo di vita e poi esegue le operazioni necessarie quando si verificano queste modifiche.
La libreria del ciclo di vita è composta da tre parti principali:
- Proprietari del ciclo di vita, ovvero i componenti che hanno (e quindi "possiedono") un ciclo di vita.
ActivityeFragmentsono proprietari del ciclo di vita. I proprietari del ciclo di vita implementano l'interfacciaLifecycleOwner. - La classe
Lifecycle, che contiene lo stato effettivo di un proprietario del ciclo di vita e attiva eventi quando si verificano modifiche del ciclo di vita. - Osservatori del ciclo di vita, che osservano lo stato del ciclo di vita ed eseguono attività quando il ciclo di vita cambia. Gli osservatori del ciclo di vita implementano l'interfaccia
LifecycleObserver.
In questa attività, converti l'app DessertClicker in modo che utilizzi la libreria del ciclo di vita Android e scopri come la libreria semplifica la gestione dei cicli di vita di attività e frammenti Android.
Passaggio 1: trasforma DessertTimer in un LifecycleObserver
Un aspetto fondamentale della libreria del ciclo di vita è il concetto di osservazione del ciclo di vita. L'osservazione consente alle classi (ad esempio DessertTimer) di conoscere il ciclo di vita dell'attività o del fragment e di avviarsi e arrestarsi in risposta alle modifiche di questi stati del ciclo di vita. Con un osservatore del ciclo di vita, puoi rimuovere la responsabilità di avviare e arrestare gli oggetti dai metodi di attività e frammento.
- Apri il corso
DesertTimer.kt. - Modifica la firma della classe
DessertTimerin modo che sia simile a questa:
class DessertTimer(lifecycle: Lifecycle) : LifecycleObserver {Questa nuova definizione di classe svolge due funzioni:
- Il costruttore accetta un oggetto
Lifecycle, ovvero il ciclo di vita che il timer sta osservando. - La definizione della classe implementa l'interfaccia
LifecycleObserver.
- Sotto la variabile
runnable, aggiungi un bloccoinitalla definizione della classe. Nel bloccoinit, utilizza il metodoaddObserver()per connettere l'oggetto ciclo di vita passato dal proprietario (l'attività) a questa classe (l'osservatore).
init {
lifecycle.addObserver(this)
}- Annota
startTimer()con@OnLifecycleEvent annotatione utilizza l'evento del ciclo di vitaON_START. Tutti gli eventi del ciclo di vita che l'osservatore del ciclo di vita può osservare si trovano nella classeLifecycle.Event.
@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun startTimer() {- Fai la stessa cosa per
stopTimer(), utilizzando l'eventoON_STOP:
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun stopTimer()Passaggio 2: modifica MainActivity
La tua classe MainActivity è già proprietaria del ciclo di vita per ereditarietà, perché la superclasse FragmentActivity implementa LifecycleOwner. Pertanto, non devi fare nulla per rendere il ciclo di vita dell'attività consapevole. Tutto ciò che devi fare è passare l'oggetto del ciclo di vita dell'attività al costruttore DessertTimer.
- Apri
MainActivity. Nel metodoonCreate(), modifica l'inizializzazione diDessertTimerin modo da includerethis.lifecycle:
dessertTimer = DessertTimer(this.lifecycle)La proprietà lifecycle dell'attività contiene l'oggetto Lifecycle di proprietà di questa attività.
- Rimuovi la chiamata a
startTimer()inonCreate()e la chiamata astopTimer()inonStop(). Non devi più dire aDessertTimercosa fare nell'attività, perché oraDessertTimerosserva il ciclo di vita e riceve una notifica automatica quando lo stato del ciclo di vita cambia. In questi callback, tutto ciò che devi fare è registrare un messaggio. - Compila ed esegui l'app e apri Logcat. Nota che il timer è stato avviato, come previsto.

- Fai clic sul pulsante Home per mettere l'app in background. Nota che il timer ha smesso di funzionare, come previsto.
Cosa succede alla tua app e ai relativi dati se Android la chiude mentre è in background? È importante comprendere questo caso limite complesso.
Quando la tua app passa in background, non viene eliminata, ma solo interrotta e in attesa che l'utente torni a utilizzarla. Tuttavia, una delle principali preoccupazioni del sistema operativo Android è garantire il corretto funzionamento dell'attività in primo piano. Ad esempio, se l'utente utilizza un'app GPS per aiutarlo a prendere un autobus, è importante eseguire il rendering dell'app GPS rapidamente e continuare a mostrare le indicazioni. È meno importante mantenere in esecuzione senza problemi in background l'app DessertClicker, che l'utente potrebbe non aver utilizzato per alcuni giorni.
Android regola le app in background in modo che l'app in primo piano possa essere eseguita senza problemi. Ad esempio, Android limita la quantità di elaborazione che le app in esecuzione in background possono eseguire.
A volte Android chiude persino un intero processo dell'app, che include ogni attività associata all'app. Android esegue questo tipo di chiusura quando il sistema è sotto stress e rischia di subire ritardi visivi, quindi a questo punto non vengono eseguiti callback o codice aggiuntivi. Il processo dell'app viene semplicemente chiuso, silenziosamente, in background. Tuttavia, per l'utente non sembra che l'app sia stata chiusa. Quando l'utente torna a un'app che il sistema operativo Android ha chiuso, Android la riavvia.
In questa attività, simulerai l'arresto di un processo Android ed esaminerai cosa succede alla tua app quando viene riavviata.
Passaggio 1: utilizza adb per simulare l'arresto di un processo
Android Debug Bridge (adb) è uno strumento a riga di comando che ti consente di inviare istruzioni agli emulatori e ai dispositivi collegati al computer. In questo passaggio, utilizzi adb per chiudere il processo dell'app e vedere cosa succede quando Android arresta l'app.
- Compila ed esegui l'app. Fai clic sulla tortina alcune volte.
- Premi il pulsante Home per mettere l'app in background. L'app è ora arrestata e potrebbe essere chiusa se Android ha bisogno delle risorse che sta utilizzando.
- In Android Studio, fai clic sulla scheda Terminale per aprire il terminale della riga di comando.

- Digita
adbe premi Invio.
Se vedi molti output che iniziano conAndroid Debug Bridge version X.XX.Xe terminano contags to be used by logcat (see logcat —help), tutto va bene. Se invece vediadb: command not found, assicurati che il comandoadbsia disponibile nel percorso di esecuzione. Per istruzioni, consulta la sezione "Aggiungere adb al percorso di esecuzione" nel capitolo Utilità. - Copia e incolla questo commento nella riga di comando e premi Invio:
adb shell am kill com.example.android.dessertclickerQuesto comando indica a tutti i dispositivi o emulatori connessi di interrompere il processo con il nome del pacchetto dessertclicker, ma solo se l'app è in background. Poiché l'app era in background, sullo schermo del dispositivo o dell'emulatore non viene visualizzato nulla che indichi che il processo è stato interrotto. In Android Studio, fai clic sulla scheda Esegui per visualizzare il messaggio "Applicazione terminata". Fai clic sulla scheda Logcat per vedere che il callback onDestroy() non è mai stato eseguito e che la tua attività è semplicemente terminata.
- Utilizza la schermata Recenti per tornare all'app. L'app viene visualizzata in Recenti sia che sia stata messa in background sia che sia stata interrotta del tutto. Quando utilizzi la schermata Recenti per tornare all'app, l'attività viene riavviata. L'attività passa attraverso l'intero insieme di callback del ciclo di vita di avvio, incluso
onCreate(). - Nota che quando l'app è stata riavviata, il "punteggio" (sia il numero di dolci venduti sia il totale in dollari) è stato ripristinato ai valori predefiniti (0). Se Android ha chiuso la tua app, perché non ha salvato il suo stato?
Quando il sistema operativo riavvia l'app, Android fa del suo meglio per ripristinarne lo stato precedente. Android prende lo stato di alcune delle tue visualizzazioni e lo salva in un bundle ogni volta che esci dall'attività. Alcuni esempi di dati salvati automaticamente sono il testo in un EditText (purché abbia un ID impostato nel layout) e lo stack indietro dell'attività.
Tuttavia, a volte il sistema operativo Android non conosce tutti i tuoi dati. Ad esempio, se nell'app DessertClicker hai una variabile personalizzata comerevenue, il sistema operativo Android non conosce questi dati né la loro importanza per la tua attività. Devi aggiungere questi dati al bundle manualmente.
Passaggio 2: utilizza onSaveInstanceState() per salvare i dati del bundle
Il metodo onSaveInstanceState() è il callback che utilizzi per salvare i dati che potrebbero servirti se il sistema operativo Android distrugge la tua app. Nel diagramma dei callback del ciclo di vita, onSaveInstanceState() viene chiamato dopo l'interruzione dell'attività. Viene chiamato ogni volta che la tua app passa in background.

Considera la chiamata onSaveInstanceState() come una misura di sicurezza: ti dà la possibilità di salvare una piccola quantità di informazioni in un bundle quando la tua attività esce dal primo piano. Il sistema salva questi dati ora perché, se aspettasse di chiudere l'app, il sistema operativo potrebbe essere sotto pressione delle risorse. Il salvataggio dei dati ogni volta garantisce che i dati di aggiornamento nel bundle siano disponibili per il ripristino, se necessario.
- In
MainActivity, esegui l'override del callbackonSaveInstanceState()e aggiungi un'istruzione di logTimber.
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
Timber.i("onSaveInstanceState Called")
}- Compila ed esegui l'app, quindi fai clic sul pulsante Home per metterla in background. Tieni presente che il callback
onSaveInstanceState()si verifica subito dopoonPause()eonStop():
- All'inizio del file, appena prima della definizione della classe, aggiungi queste costanti:
const val KEY_REVENUE = "revenue_key"
const val KEY_DESSERT_SOLD = "dessert_sold_key"
const val KEY_TIMER_SECONDS = "timer_seconds_key"Utilizzerai queste chiavi sia per salvare che per recuperare i dati dal bundle dello stato dell'istanza.
- Scorri verso il basso fino a
onSaveInstanceState()e nota il parametrooutState, di tipoBundle.
Un bundle è una raccolta di coppie chiave-valore, in cui le chiavi sono sempre stringhe. Puoi inserire valori primitivi, come valoriinteboolean, nel bundle.
Poiché il sistema mantiene questo bundle nella RAM, è consigliabile mantenere i dati nel bundle di piccole dimensioni. Anche le dimensioni di questo bundle sono limitate, anche se variano da dispositivo a dispositivo. In genere, dovresti memorizzare molto meno di 100.000 elementi, altrimenti rischi di arrestare l'app con l'erroreTransactionTooLargeException. - In
onSaveInstanceState(), inserisci il valorerevenue(un numero intero) nel bundle con il metodoputInt():
outState.putInt(KEY_REVENUE, revenue)Il metodo putInt() (e metodi simili della classe Bundle come putFloat() e putString()) accetta due argomenti: una stringa per la chiave (la costante KEY_REVENUE) e il valore effettivo da salvare.
- Ripeti la stessa procedura con il numero di dolci venduti e lo stato del timer:
outState.putInt(KEY_DESSERT_SOLD, dessertsSold)
outState.putInt(KEY_TIMER_SECONDS, dessertTimer.secondsCount)Passaggio 3: utilizza onCreate() per ripristinare i dati del bundle
- Scorri verso l'alto fino a
onCreate()ed esamina la firma del metodo:
override fun onCreate(savedInstanceState: Bundle) {Tieni presente che onCreate() riceve un Bundle ogni volta che viene chiamato. Quando l'attività viene riavviata a causa dell'arresto di un processo, il bundle che hai salvato viene passato a onCreate(). Se la tua attività è iniziata da poco, questo pacchetto in onCreate() è null. Quindi, se il bundle non è null, sai che stai "ricreando " l'attività da un punto noto in precedenza.
- Aggiungi questo codice a
onCreate(), dopo la configurazione diDessertTimer:
if (savedInstanceState != null) {
revenue = savedInstanceState.getInt(KEY_REVENUE, 0)
}Il test per null determina se sono presenti dati nel bundle o se il bundle è null, il che a sua volta indica se l'app è stata avviata da zero o è stata ricreata dopo un arresto. Questo test è un pattern comune per il ripristino dei dati dal bundle.
Nota che la chiave che hai utilizzato qui (KEY_REVENUE) è la stessa che hai utilizzato per putInt(). Per assicurarti di utilizzare la stessa chiave ogni volta, la best practice consiste nel definire queste chiavi come costanti. Utilizzi getInt() per estrarre i dati dal bundle, proprio come hai utilizzato putInt() per inserirli. Il metodo getInt() accetta due argomenti:
- Una stringa che funge da chiave, ad esempio
"key_revenue"per il valore delle entrate. - Un valore predefinito nel caso in cui non esista un valore per quella chiave nel bundle.
L'intero ottenuto dal bundle viene quindi assegnato alla variabile revenue e l'interfaccia utente utilizzerà questo valore.
- Aggiungi i metodi
getInt()per ripristinare il numero di dolci venduti e il valore del timer:
if (savedInstanceState != null) {
revenue = savedInstanceState.getInt(KEY_REVENUE, 0)dessertsSold = savedInstanceState.getInt(KEY_DESSERT_SOLD, 0)
dessertTimer.secondsCount =
savedInstanceState.getInt(KEY_TIMER_SECONDS, 0)
}- Compila ed esegui l'app. Premi la tortina almeno cinque volte finché non si trasforma in una ciambella. Fai clic su Home per mettere l'app in background.
- Nella scheda Terminale di Android Studio, esegui
adbper arrestare il processo dell'app.
adb shell am kill com.example.android.dessertclicker- Utilizza la schermata Recenti per tornare all'app. Nota che questa volta l'app torna con i valori corretti di entrate e dolci venduti del bundle. Ma nota anche che il dolce è tornato a essere un cupcake. Per assicurarti che l'app venga ripristinata dopo l'arresto esattamente come è stata lasciata, devi fare un'altra cosa.
- In
MainActivity, esamina il metodoshowCurrentDessert(). Tieni presente che questo metodo determina quale immagine del dolce deve essere visualizzata nell'attività in base al numero attuale di dolci venduti e all'elenco dei dolci nella variabileallDesserts.
for (dessert in allDesserts) {
if (dessertsSold >= dessert.startProductionAmount) {
newDessert = dessert
}
else break
}Questo metodo si basa sul numero di dolci venduti per scegliere l'immagine giusta. Pertanto, non devi fare nulla per memorizzare un riferimento all'immagine nel bundle in onSaveInstanceState(). In questo pacchetto, memorizzi già il numero di dolci venduti.
- In
onCreate(), nel blocco che ripristina lo stato dal bundle, chiamashowCurrentDessert():
if (savedInstanceState != null) {
revenue = savedInstanceState.getInt(KEY_REVENUE, 0)
dessertsSold = savedInstanceState.getInt(KEY_DESSERT_SOLD, 0)
dessertTimer.secondsCount =
savedInstanceState.getInt(KEY_TIMER_SECONDS, 0)
showCurrentDessert()
}- Compila ed esegui l'app e mettila in background. Usa
adbper interrompere la procedura. Utilizza la schermata Recenti per tornare all'app. Nota che ora i valori per i dolci venduti, le entrate totali e l'immagine dei dolci sono stati ripristinati correttamente.
Esiste un ultimo caso speciale nella gestione del ciclo di vita di attività e frammenti che è importante comprendere: in che modo le modifiche alla configurazione influiscono sul ciclo di vita di attività e frammenti.
Una modifica della configurazione si verifica quando lo stato del dispositivo cambia in modo così radicale che il modo più semplice per il sistema di risolvere la modifica è spegnere completamente e ricreare l'attività. Ad esempio, se l'utente cambia la lingua del dispositivo, potrebbe essere necessario modificare l'intero layout per adattarsi a diverse direzioni del testo. Se l'utente collega il dispositivo a una base di ricarica o aggiunge una tastiera fisica, il layout dell'app potrebbe dover sfruttare una dimensione o un layout di visualizzazione diverso. Se l'orientamento del dispositivo cambia, ad esempio se viene ruotato dalla modalità verticale a quella orizzontale o viceversa, potrebbe essere necessario modificare il layout per adattarlo al nuovo orientamento.
Passaggio 1: esplora la rotazione del dispositivo e i callback del ciclo di vita
- Compila ed esegui l'app e apri Logcat.
- Ruota il dispositivo o l'emulatore in modalità orizzontale. Puoi ruotare l'emulatore a sinistra o a destra con i pulsanti di rotazione oppure con i tasti
Controle Freccia (Commande Freccia su Mac).
- Esamina l'output in Logcat. Filtra l'output in base a
MainActivity.
Tieni presente che quando il dispositivo o l'emulatore ruota lo schermo, il sistema chiama tutti i callback del ciclo di vita per arrestare l'attività. Poi, quando l'attività viene ricreata, il sistema chiama tutti i callback del ciclo di vita per avviare l'attività. - In
MainActivity, commenta l'intero metodoonSaveInstanceState(). - Compila ed esegui di nuovo l'app. Fai clic sulla tortina alcune volte e ruota il dispositivo o l'emulatore. Questa volta, quando il dispositivo viene ruotato e l'attività viene chiusa e ricreata, l'attività viene avviata con i valori predefiniti.
Quando si verifica una modifica alla configurazione, Android utilizza lo stesso bundle di stato dell'istanza che hai imparato nella sezione precedente per salvare e ripristinare lo stato dell'app. Come per l'arresto di un processo, utilizzaonSaveInstanceState()per inserire i dati dell'app nel bundle. Poi ripristina i dati inonCreate()per evitare di perdere i dati sullo stato dell'attività se il dispositivo viene ruotato. - In
MainActivity, rimuovi il commento dal metodoonSaveInstanceState(), esegui l'app, fai clic sul cupcake e ruota l'app o il dispositivo. Nota che questa volta i dati sui dolci vengono conservati durante la rotazione delle attività.
Progetto Android Studio: DessertClickerFinal
Suggerimenti per il ciclo di vita
- Se configuri o avvii qualcosa in un callback del ciclo di vita, interrompi o rimuovi l'elemento nel callback corrispondente. Interrompendo l'operazione, ti assicuri che non continui a essere eseguita quando non è più necessaria. Ad esempio, se imposti un timer in
onStart(), devi metterlo in pausa o interromperlo inonStop(). - Utilizza
onCreate()solo per inizializzare le parti dell'app che vengono eseguite una sola volta, al primo avvio dell'app. UtilizzaonStart()per avviare le parti dell'app che vengono eseguite sia all'avvio dell'app sia ogni volta che l'app torna in primo piano.
Raccolta del ciclo di vita
- Utilizza la libreria del ciclo di vita di Android per spostare il controllo del ciclo di vita dall'attività o dal fragment al componente effettivo che deve essere consapevole del ciclo di vita.
- I proprietari del ciclo di vita sono componenti che hanno (e quindi "possiedono") cicli di vita, inclusi
ActivityeFragment. I proprietari del ciclo di vita implementano l'interfacciaLifecycleOwner. - Gli osservatori del ciclo di vita prestano attenzione allo stato attuale del ciclo di vita ed eseguono le attività quando il ciclo di vita cambia. Gli osservatori del ciclo di vita implementano l'interfaccia
LifecycleObserver. Lifecyclecontengono gli stati del ciclo di vita effettivi e attivano eventi quando il ciclo di vita cambia.
Per creare una classe consapevole del ciclo di vita:
- Implementa l'interfaccia
LifecycleObservernelle classi che devono essere consapevoli del ciclo di vita. - Inizializza una classe di osservatore del ciclo di vita con l'oggetto ciclo di vita dell'attività o del fragmento.
- Nella classe dell'observer del ciclo di vita, annota i metodi sensibili al ciclo di vita con la modifica dello stato del ciclo di vita a cui sono interessati.
Ad esempio, l'annotazione@OnLifecycleEvent(Lifecycle.Event.ON_START)indica che il metodo sta monitorando l'evento del ciclo di vitaonStart.
Chiusure dei processi e salvataggio dello stato dell'attività
- Android regola le app in esecuzione in background in modo che l'app in primo piano possa essere eseguita senza problemi. Questo regolamento prevede la limitazione della quantità di elaborazione che le app in background possono eseguire e, a volte, anche l'arresto dell'intero processo dell'app.
- L'utente non può sapere se il sistema ha chiuso un'app in background. L'app viene comunque visualizzata nella schermata Recenti e dovrebbe riavviarsi nello stesso stato in cui l'utente l'ha lasciata.
- Android Debug Bridge (
adb) è uno strumento a riga di comando che ti consente di inviare istruzioni agli emulatori e ai dispositivi collegati al computer. Puoi utilizzareadbper simulare l'arresto di un processo nella tua app. - Quando Android arresta il processo dell'app, il metodo del ciclo di vita
onDestroy()non viene chiamato. L'app si arresta.
Preservare lo stato di attività e frammenti
- Quando l'app passa in background, subito dopo la chiamata di
onStop(), i dati dell'app vengono salvati in un bundle. Alcuni dati delle app, ad esempio i contenuti di unEditText, vengono salvati automaticamente. - Il bundle è un'istanza di
Bundle, che è una raccolta di chiavi e valori. Le chiavi sono sempre stringhe. - Utilizza il callback
onSaveInstanceState()per salvare altri dati nel bundle che vuoi conservare, anche se l'app è stata chiusa automaticamente. Per inserire i dati nel bundle, utilizza i metodi del bundle che iniziano conput, ad esempioputInt(). - Puoi estrarre i dati dal bundle con il metodo
onRestoreInstanceState()o, più comunemente, cononCreate(). Il metodoonCreate()ha un parametrosavedInstanceStateche contiene il bundle. - Se la variabile
savedInstanceStatecontienenull, l'attività è stata avviata senza un bundle di stato e non ci sono dati di stato da recuperare. - Per recuperare i dati dal bundle con una chiave, utilizza i metodi
Bundleche iniziano conget, ad esempiogetInt().
Modifiche alla configurazione
- Una modifica della configurazione si verifica quando lo stato del dispositivo cambia in modo così radicale che il modo più semplice per il sistema di risolvere la modifica è spegnere e ricreare l'attività.
- L'esempio più comune di modifica della configurazione si verifica quando l'utente ruota il dispositivo dalla modalità verticale a quella orizzontale o viceversa. Una modifica alla configurazione può verificarsi anche quando la lingua del dispositivo cambia o viene collegata una tastiera hardware.
- Quando si verifica una modifica alla configurazione, Android richiama tutti i callback di arresto del ciclo di vita dell'attività. Android riavvia l'attività da zero, eseguendo tutti i callback di avvio del ciclo di vita.
- Quando Android arresta un'app a causa di una modifica alla configurazione, riavvia l'attività con il bundle di stato disponibile per
onCreate(). - Come per l'arresto del processo, salva lo stato dell'app nel bundle in
onSaveInstanceState().
Corso Udacity:
Documentazione per sviluppatori Android:
- Attività (guida API)
Activity(riferimento API)- Comprendere il ciclo di vita dell'attività
- Gestione dei cicli di vita con componenti sensibili al ciclo di vita
LifecycleOwnerLifecycleLifecycleObserveronSaveInstanceState()- Gestire le modifiche alla configurazione
- Salvataggio degli stati dell'UI
Altro:
- Timber (GitHub)
Questa sezione elenca i possibili compiti a casa per gli studenti che seguono questo codelab nell'ambito di un corso guidato da un insegnante. Spetta all'insegnante:
- Assegna i compiti, se richiesto.
- Comunica agli studenti come inviare i compiti.
- Valuta i compiti a casa.
Gli insegnanti possono utilizzare questi suggerimenti nella misura che ritengono opportuna e sono liberi di assegnare qualsiasi altro compito a casa che ritengono appropriato.
Se stai seguendo questo codelab in autonomia, sentiti libero di utilizzare questi compiti per casa per mettere alla prova le tue conoscenze.
Cambiare un'app
Apri l'app DiceRoller della lezione 1. Se non l'hai ancora installata, puoi scaricarla da qui. Compila ed esegui l'app e tieni presente che se ruoti il dispositivo, il valore corrente del dado viene perso. Implementa onSaveInstanceState() per conservare questo valore nel bundle e ripristinarlo in onCreate().
Rispondi a queste domande
Domanda 1
La tua app contiene una simulazione fisica che richiede un calcolo complesso per essere visualizzata. Poi l'utente riceve una telefonata. Quale delle seguenti affermazioni è vera?
- Durante la chiamata, devi continuare a calcolare le posizioni degli oggetti nella simulazione fisica.
- Durante la chiamata, devi interrompere il calcolo delle posizioni degli oggetti nella simulazione fisica.
Domanda 2
Quale metodo del ciclo di vita devi eseguire l'override per mettere in pausa la simulazione quando l'app non è sullo schermo?
onDestroy()onStop()onPause()onSaveInstanceState()
Domanda 3
Per rendere una classe consapevole del ciclo di vita tramite la libreria del ciclo di vita di Android, quale interfaccia deve implementare la classe?
LifecycleLifecycleOwnerLifecycle.EventLifecycleObserver
Domanda 4
In quali circostanze il metodo onCreate() nella tua attività riceve un Bundle con dati (ovvero, Bundle non è null)? Potrebbe essere applicabile più di una risposta.
- L'attività viene riavviata dopo la rotazione del dispositivo.
- L'attività viene avviata da zero.
- L'attività viene ripresa dopo il ritorno dal background.
- Il dispositivo viene riavviato.
Inizia la lezione successiva:
Per i link ad altri codelab di questo corso, consulta la pagina di destinazione dei codelab di Android Kotlin Fundamentals.