1. Ti diamo il benvenuto
Questo codelab pratico fa parte della Unità 1: Inizia nel corso Android Developer Fundamentals (versione 2). Per ottenere il massimo da questo corso, ti consigliamo di svolgere i codelab in sequenza:
- Per l'elenco completo dei codelab del corso, consulta Codelab per gli aspetti fondamentali per gli sviluppatori Android (v2).
- Per informazioni dettagliate sul corso, inclusi i link a tutti i capitoli, le app e le diapositive sui concetti, consulta Android Developer Fundamentals (versione 2).
Introduzione
In questo esercizio pratico scoprirai di più sul ciclo di vita delle attività. Il ciclo di vita è l'insieme di stati in cui può trovarsi un'attività durante tutto il suo ciclo di vita, dalla creazione alla distruzione e al recupero delle risorse da parte del sistema. Quando un utente passa da un'attività all'altra nella tua app (nonché dentro e fuori dalla tua app), le attività passano da uno stato all'altro nel loro ciclo di vita.
Ogni fase del ciclo di vita di un'attività ha un metodo di callback corrispondente: onCreate(), onStart(), onPause() e così via. Quando un'attività cambia stato, viene invocato il metodo di callback associato. Hai già visto uno di questi metodi: onCreate(). Sostituendo uno dei metodi di callback del ciclo di vita nei tuoi classi Activity, puoi modificare il comportamento predefinito dell'attività in risposta alle azioni dell'utente o del sistema.
Lo stato dell'attività può cambiare anche in risposta alle modifiche alla configurazione del dispositivo, ad esempio quando l'utente ruota il dispositivo da verticale a orizzontale. Quando si verificano queste modifiche alla configurazione, l'attività viene distrutta e ricreata nel suo stato predefinito e l'utente potrebbe perdere le informazioni inserite. Per evitare di confondere gli utenti, è importante sviluppare l'app in modo da prevenire la perdita imprevista di dati. Più avanti in questo esercizio pratico, eseguirai esperimenti sulle modifiche alla configurazione e imparerai a preservare lo stato di un'attività in risposta alle modifiche alla configurazione del dispositivo e ad altri eventi del ciclo di vita dell'attività.
In questo esercizio pratico aggiungi istruzioni di logging all'app TwoActivities e osservi le modifiche al ciclo di vita delle attività durante l'utilizzo dell'app. Poi inizi a utilizzare queste modifiche ed esamini come gestire l'input utente in queste condizioni.
Prerequisiti
Dovresti riuscire a:
- Crea ed esegui un progetto di app in Android Studio.
- Aggiungi istruzioni di log all'app e visualizzale nel riquadro Logcat.
- Comprendi e utilizza un'attività e uno scopo e interagisci con essi in tutta sicurezza.
Obiettivi didattici
- Come funziona il ciclo di vita dell'attività.
- Quando un'attività viene avviata, messa in pausa, interrotta e distrutta.
- Informazioni sui metodi di callback del ciclo di vita associati alle modifiche dell'attività.
- L'effetto di azioni (ad esempio modifiche alla configurazione) che possono comportare eventi del ciclo di vita dell'attività.
- Come mantenere lo stato dell'attività negli eventi del ciclo di vita.
In questo lab proverai a:
- Aggiungi codice all'app TwoActivities dell'esercizio pratico precedente per implementare i vari callback del ciclo di vita dell'attività in modo da includere le istruzioni di logging.
- Osserva le modifiche dello stato durante l'esecuzione dell'app e mentre interagisci con ogni attività al suo interno.
- Modifica l'app in modo da conservare lo stato dell'istanza di un'attività che viene ricreata in modo imprevisto in risposta al comportamento dell'utente o alla modifica della configurazione sul dispositivo.
2. Panoramica app
In questo esercizio pratico aggiungi elementi all'app TwoActivities. L'app ha un aspetto e un comportamento simili a quelli dell'ultimo codelab. Contiene due implementazioni di Attività e offre all'utente la possibilità di inviare messaggi tra le due. Le modifiche apportate all'app in questo esercizio pratico non influiranno sul comportamento visibile dell'utente.
3. 3. Attività 1: aggiungi i callback del ciclo di vita a TwoActivities
In questa attività implementerai tutti i metodi di callback del ciclo di vita dell'attività per stampare i messaggi in logcat quando vengono richiamati. Questi messaggi di log ti consentono di vedere quando il ciclo di vita dell'attività cambia stato e in che modo queste modifiche dello stato del ciclo di vita influiscono sulla tua app durante l'esecuzione.
1.1 (Facoltativo) Copia il progetto DueAttività
Per le attività di questo esercizio pratico, modificherai il progetto TwoActivities esistente che hai creato nell'ultimo esercizio pratico. Se preferisci mantenere invariato il progetto TwoActivities precedente, segui i passaggi descritti nell'Appendice: utilità per creare una copia del progetto.
1.2 Implementa i callback in MainActivity
- Apri il progetto TwoActivities in Android Studio e apri MainActivity nel riquadro Progetto > Android.
- Nel metodo onCreate(), aggiungi le seguenti istruzioni di log:
Log.d(LOG_TAG, "-------");
Log.d(LOG_TAG, "onCreate");
- Aggiungi un'override per il callback onStart(), con un'istruzione nel log per l'evento:
@Override
public void onStart(){
super.onStart();
Log.d(LOG_TAG, "onStart");
}
Per una scorciatoia, seleziona Codice > Sostituisci metodi in Android Studio. Viene visualizzata una finestra di dialogo con tutti i possibili metodi che puoi sostituire nel corso. La scelta di uno o più metodi di callback dall'elenco inserisce un modello completo per questi metodi, inclusa la chiamata richiesta al superclasse.
- Utilizza il metodo onStart() come modello per implementare i callback del ciclo di vita onPause(), onRestart(), onResume(), onStop() e onDestroy()
Tutti i metodi di callback hanno le stesse firme (tranne il nome). Se copi e incolli onStart() per creare questi altri metodi di callback, non dimenticare di aggiornare i contenuti per chiamare il metodo corretto nel superclasse e di registrare il metodo corretto.
- Esegui l'app.
- Fai clic sulla scheda Logcat nella parte inferiore di Android Studio per visualizzare il riquadro Logcat. Dovresti vedere tre messaggi di log che mostrano i tre stati del ciclo di vita che l'attività ha attraversato all'avvio:
D/MainActivity: -------
D/MainActivity: onCreate
D/MainActivity: onStart
D/MainActivity: onResume
1.3 Implementa i callback del ciclo di vita in SecondActivity
Ora che hai implementato i metodi di callback del ciclo di vita per MainActivity, fai lo stesso per SecondActivity.
- Apri SecondActivity.
- Nella parte superiore della classe, aggiungi una costante per la variabile LOG_TAG:
private static final String LOG_TAG = SecondActivity.class.getSimpleName();
- Aggiungi i callback del ciclo di vita e le istruzioni di log alla seconda attività. Puoi copiare e incollare i metodi di callback da MainActivity.
- Aggiungi un'istruzione di log al metodo returnReply() appena prima del metodo finish():
Log.d(LOG_TAG, "End SecondActivity");
1.4 Osserva il log durante l'esecuzione dell'app**
- Esegui l'app.
- Fai clic sulla scheda Logcat nella parte inferiore di Android Studio per visualizzare il riquadro Logcat.
- Inserisci Attività nella casella di ricerca. Il logcat di Android può essere molto lungo e disordinato. Poiché la variabile LOG_TAG in ogni classe contiene le parole MainActivity o SecondActivity, questa parola chiave ti consente di filtrare il log in base solo agli elementi che ti interessano.
Fai esperimenti con l'utilizzo della tua app e tieni presente gli eventi del ciclo di vita che si verificano in risposta a diverse azioni. In particolare, prova a procedere nel seguente modo:
- Utilizza l'app normalmente (invia un messaggio, rispondi con un altro messaggio).
- Usa il pulsante Indietro per tornare dalla seconda attività all'attività principale.
- Usa la Freccia su nella barra delle app per tornare dalla seconda attività all'attività principale.
- Ruota il dispositivo sia nell'attività principale che nella seconda attività in momenti diversi della tua app e osserva cosa succede nel log e sullo schermo.
- Premi il pulsante Panoramica (il pulsante quadrato a destra di Home) e chiudi l'app (tocca la X).
- Torna alla schermata Home e riavvia l'app.
Suggerimento: se esegui l'app in un emulatore, puoi simulare la rotazione con Ctrl+F11 o Ctrl+Funzione+F11.
Codice della soluzione dell'attività 1
I seguenti snippet di codice mostrano il codice della soluzione per la prima attività.
MainActivity
I seguenti snippet di codice mostrano il codice aggiunto in MainActivity, ma non l'intera classe.
Il metodo onCreate():
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Log the start of the onCreate() method.
Log.d(LOG_TAG, "-------");
Log.d(LOG_TAG, "onCreate");
// Initialize all the view variables.
mMessageEditText = findViewById(R.id.editText_main);
mReplyHeadTextView = findViewById(R.id.text_header_reply);
mReplyTextView = findViewById(R.id.text_message_reply);
}
Gli altri metodi del ciclo di vita:
@Override
protected void onStart() {
super.onStart();
Log.d(LOG_TAG, "onStart");
}
@Override
protected void onPause() {
super.onPause();
Log.d(LOG_TAG, "onPause");
}
@Override
protected void onRestart() {
super.onRestart();
Log.d(LOG_TAG, "onRestart");
}
@Override
protected void onResume() {
super.onResume();
Log.d(LOG_TAG, "onResume");
}
@Override
protected void onStop() {
super.onStop();
Log.d(LOG_TAG, "onStop");
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.d(LOG_TAG, "onDestroy");
}
SecondActivity
Gli snippet di codice seguenti mostrano il codice aggiunto in SecondActivity, ma non l'intera classe.
Nella parte superiore della classe SecondActivity:
private static final String LOG_TAG = SecondActivity.class.getSimpleName();
Il metodo returnReply():
public void returnReply(View view) {
String reply = mReply.getText().toString();
Intent replyIntent = new Intent();
replyIntent.putExtra(EXTRA_REPLY, reply);
setResult(RESULT_OK, replyIntent);
Log.d(LOG_TAG, "End SecondActivity");
finish();
}
Gli altri metodi del ciclo di vita:
Come per MainActivity, sopra.
4. 4. Attività 2: salva e ripristina lo stato dell'istanza Activity
A seconda delle risorse di sistema e del comportamento degli utenti, ogni attività nella tua app potrebbe essere distrutta e ricostruita molto più di frequente di quanto potresti pensare.
Potresti aver notato questo comportamento nell'ultima sezione quando hai ruotato il dispositivo o l'emulatore. La rotazione del dispositivo è un esempio di modifica della configurazione del dispositivo. Sebbene la rotazione sia la più comune, tutte le modifiche alla configurazione comportano l'eliminazione dell'attività corrente e la sua ricreazione come se fosse nuova. Se non tieni conto di questo comportamento nel codice, quando si verifica una modifica di configurazione, il layout dell'attività potrebbe tornare all'aspetto e ai valori iniziali predefiniti e gli utenti potrebbero perdere il punto in cui si trovavano, i dati o lo stato di avanzamento nella tua app.
Lo stato di ogni attività viene memorizzato come insieme di coppie chiave/valore in un oggetto Bundle chiamato stato dell'istanza dell'attività. Il sistema salva le informazioni sullo stato predefinito nel bundle dello stato dell'istanza appena prima dell'interruzione dell'attività e le passa al nuovo bundle dell'attività da ripristinare.
Per evitare di perdere dati in un'attività quando viene distrutta e ricreata in modo imprevisto, devi implementare il metodo onSaveInstanceState(). Il sistema chiama questo metodo nella tua attività (tra onPause() e onStop()) quando è possibile che l'attività venga distrutta e ricreata.
I dati salvati nello stato dell'istanza sono specifici solo per questa istanza di questa attività specifica durante la sessione dell'app corrente. Quando interrompi e riavvii una nuova sessione dell'app, lo stato dell'istanza dell'attività viene perso e l'attività torna all'aspetto predefinito. Se devi salvare i dati utente tra le sessioni dell'app, utilizza le preferenze condivise o un database. Imparerai a utilizzare entrambi in un esercizio pratico successivo.
2.1 Salvare lo stato dell'istanza dell'attività con onSaveInstanceState()
Potresti aver notato che la rotazione del dispositivo non influisce sullo stato della seconda attività. Questo perché il layout e lo stato della seconda attività vengono generati dal layout e dall'Intent che l'hanno attivata. Anche se l'attività viene ricreata, l'intent è ancora presente e i dati al suo interno vengono utilizzati ogni volta che viene chiamato il metodo onCreate() nella seconda attività.
Inoltre, potresti notare che in ogni attività, il testo digitato negli elementi EditText di un messaggio o di una risposta viene mantenuto anche quando il dispositivo è ruotato. Questo accade perché le informazioni sullo stato di alcuni elementi View nel layout vengono salvate automaticamente durante le modifiche alla configurazione e il valore corrente di un EditText è uno di questi casi.
Pertanto, l'unico stato dell'attività che ti interessa sono gli elementi TextView per l'intestazione della risposta e il testo della risposta nell'attività principale. Entrambi gli elementi TextView sono invisibili per impostazione predefinita; vengono visualizzati solo quando invii un messaggio all'attività principale dalla seconda attività.
In questa attività aggiungi il codice per preservare lo stato dell'istanza di questi due elementi TextView utilizzando onSaveInstanceState().
- Apri MainActivity.
- Aggiungi questa implementazione di base di onSaveInstanceState() all'attività o utilizza Codice > Metodi di override per inserire un override di base.
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
}
- Controlla se l'intestazione è attualmente visibile e, in caso affermativo, inserisci lo stato di visibilità nel bundle di stato con il metodo putBoolean() e la chiave "reply_visible".
if (mReplyHeadTextView.getVisibility() == View.VISIBLE) {
outState.putBoolean("reply_visible", true);
}
Ricorda che l'intestazione e il testo della risposta sono contrassegnati come invisibili finché non viene inviata una risposta dalla seconda attività. Se l'intestazione è visibile, significa che ci sono dati di risposta da salvare. Tieni presente che ci interessa solo lo stato di visibilità: il testo effettivo dell'intestazione non deve essere salvato, perché non cambia mai.
- Nello stesso controllo, aggiungi il testo della risposta nel bundle.
outState.putString("reply_text",mReplyTextView.getText().toString());
Se l'intestazione è visibile, puoi presumere che sia visibile anche il messaggio di risposta. Non è necessario verificare o salvare lo stato di visibilità corrente del messaggio di risposta. Solo il testo effettivo del messaggio viene inserito nel bundle dello stato con la chiave "reply_text".
Salva lo stato solo di quegli elementi della visualizzazione che potrebbero cambiare dopo la creazione dell'attività. Gli altri elementi View dell'app (EditText, Button) possono essere ricreati dal layout predefinito in qualsiasi momento.
Tieni presente che il sistema salverà lo stato di alcuni elementi View, ad esempio i contenuti di EditText.
2.2 Ripristina lo stato dell'istanza dell'attività in onCreate()
Dopo aver salvato lo stato dell'istanza dell'attività, devi anche ripristinarlo quando l'attività viene ricreata. Puoi farlo in onCreate() o implementando il callback onRestoreInstanceState(), che viene chiamato dopo onStart() dopo la creazione dell'attività.
Nella maggior parte dei casi, il posto migliore per ripristinare lo stato dell'attività è onCreate(), per assicurarti che l'interfaccia utente, incluso lo stato, sia disponibile il prima possibile. A volte è conveniente farlo in onRestoreInstanceState() dopo che è stata completata tutta l'inizializzazione o per consentire alle sottoclassi di decidere se utilizzare l'implementazione predefinita.
- Nel metodo onCreate(), dopo l'inizializzazione delle variabili View con findViewById(), aggiungi un test per assicurarti che savedInstanceState non sia null.
// Initialize all the view variables.
mMessageEditText = findViewById(R.id.editText_main);
mReplyHeadTextView = findViewById(R.id.text_header_reply);
mReplyTextView = findViewById(R.id.text_message_reply);
// Restore the state.
if (savedInstanceState != null) {
}
Quando l'attività viene creata, il sistema passa il bundle dello stato a onCreate() come unico argomento. La prima volta che viene chiamato onCreate() e l'app viene avviata, il Bundle è nullo: non esiste uno stato la prima volta che l'app viene avviata. Le chiamate successive a onCreate() hanno un bundle compilato con i dati archiviati in onSaveInstanceState().
- All'interno di questo controllo, ottieni la visibilità corrente (true o false) dal bundle con la chiave "reply_visible".
if (savedInstanceState != null) {
boolean isVisible =
savedInstanceState.getBoolean("reply_visible");
}
- Aggiungi un test sotto la riga precedente per la variabile isVisible.
if (isVisible) {
}
Se nel bundle dello stato è presente una chiave reply_visible (e isVisible è quindi true), dovrai ripristinare lo stato.
- All'interno del test isVisible, rendi visibile l'intestazione.
mReplyHeadTextView.setVisibility(View.VISIBLE);
- Recupera il messaggio di risposta dal bundle con la chiave "reply_text" e imposta il TextView di risposta in modo che mostri questa stringa.
mReplyTextView.setText(savedInstanceState.getString("reply_text"));
- Rendi visibile anche il TextView della risposta:
mReplyTextView.setVisibility(View.VISIBLE);
- Esegui l'app. Prova a ruotare il dispositivo o l'emulatore per assicurarti che il messaggio di risposta (se presente) rimanga sullo schermo dopo la ricreazione dell'attività.
Codice della soluzione dell'attività 2
I seguenti snippet di codice mostrano il codice della soluzione per questa attività.
MainActivity
I seguenti snippet di codice mostrano il codice aggiunto in MainActivity, ma non l'intera classe.
Il metodo onSaveInstanceState():
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// If the heading is visible, message needs to be saved.
// Otherwise we're still using default layout.
if (mReplyHeadTextView.getVisibility() == View.VISIBLE) {
outState.putBoolean("reply_visible", true);
outState.putString("reply_text",
mReplyTextView.getText().toString());
}
}
Il metodo onCreate():
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d(LOG_TAG, "-------");
Log.d(LOG_TAG, "onCreate");
// Initialize all the view variables.
mMessageEditText = findViewById(R.id.editText_main);
mReplyHeadTextView = findViewById(R.id.text_header_reply);
mReplyTextView = findViewById(R.id.text_message_reply);
// Restore the saved state.
// See onSaveInstanceState() for what gets saved.
if (savedInstanceState != null) {
boolean isVisible =
savedInstanceState.getBoolean("reply_visible");
// Show both the header and the message views. If isVisible is
// false or missing from the bundle, use the default layout.
if (isVisible) {
mReplyHeadTextView.setVisibility(View.VISIBLE);
mReplyTextView.setText(savedInstanceState
.getString("reply_text"));
mReplyTextView.setVisibility(View.VISIBLE);
}
}
}
Il progetto completo:
Progetto Android Studio: TwoActivitiesLifecycle
5. Codifica
Sfida: crea una semplice app per la lista della spesa con un'attività principale per l'elenco che l'utente sta creando e una seconda attività per un elenco di articoli comuni per la spesa.
- L'attività principale deve contenere l'elenco da creare, composto da dieci elementi TextView vuoti.
- Un pulsante Aggiungi articolo nell'attività principale avvia una seconda attività contenente un elenco di articoli comuni per la spesa (formaggio, riso, mele e così via). Utilizza gli elementi Button per visualizzare gli elementi.
- La scelta di un elemento fa tornare l'utente all'attività principale e aggiorna un TextView vuoto in modo da includere l'elemento scelto.
Utilizza un'intent per passare informazioni da un'attività all'altra. Assicurati che lo stato corrente della lista della spesa venga salvato quando l'utente ruota il dispositivo.
6. Riepilogo
- Il ciclo di vita dell'attività è un insieme di stati attraverso i quali un'attività esegue la migrazione, a partire dalla prima creazione e fino al momento in cui il sistema Android recupera le risorse per l'attività.
- Quando l'utente passa da un'attività all'altra, all'interno e all'esterno della tua app, ogni attività passa da uno stato all'altro nel ciclo di vita dell'attività.
- Ogni stato nel ciclo di vita dell'attività ha un metodo di callback corrispondente che puoi sostituire nella classe Attività.
- I metodi del ciclo di vita sono onCreate(), onStart(), onPause(), onRestart(), onResume(), onStop(), onDestroy().
- L'override di un metodo di callback del ciclo di vita ti consente di aggiungere il comportamento che si verifica quando la tua attività passa a questo stato.
- Puoi aggiungere metodi di override dello scheletro alle tue classi in Android Studio con Codice > Sostituisci.
- Le modifiche alla configurazione del dispositivo, come la rotazione, comportano l'eliminazione e la successiva ricreazione dell'attività come se fosse nuova.
- Una parte dello stato dell'attività viene conservata in caso di modifica della configurazione, inclusi i valori correnti degli elementi EditText. Per tutti gli altri dati, devi salvarli esplicitamente.
- Salva lo stato dell'istanza dell'attività nel metodo onSaveInstanceState().
- I dati relativi allo stato dell'istanza vengono archiviati come semplici coppie chiave/valore in un bundle. Utilizza i metodi Bundle per inserire dati nel Bundle e recuperarli.
- Ripristina lo stato dell'istanza in onCreate(), che è il metodo preferito, o in onRestoreInstanceState(). Torna