Il ciclo di vita dell'attività

Mentre un utente esplora, esce e rientra nell'app, le istanze Activity nell'app passano attraverso stati diversi nel loro ciclo di vita. La classe Activity fornisce una serie di callback che consentono all'attività di sapere quando uno stato cambia o che il sistema sta creando, interrompendo o ripristinando un'attività o distruggendo il processo in cui si trova l'attività.

Nei metodi di callback del ciclo di vita, puoi dichiarare il comportamento dell'attività quando l'utente abbandona e rientra l'attività. Ad esempio, se stai creando un video player in streaming, potresti mettere in pausa il video e terminare la connessione di rete quando l'utente passa a un'altra app. Quando l'utente ritorna, puoi riconnetterti alla rete e consentire all'utente di riprendere il video dallo stesso punto.

Ogni callback consente di eseguire operazioni specifiche appropriate per un determinato cambio di stato. Svolgere il lavoro giusto al momento giusto e gestire correttamente le transizioni rende la tua app più solida e performante. Ad esempio, una buona implementazione dei callback del ciclo di vita può aiutare la tua app a evitare quanto segue:

  • Si arresta in modo anomalo se l'utente riceve una chiamata o passa a un'altra app mentre utilizza la tua app.
  • Consumo di risorse di sistema preziose quando l'utente non le utilizza attivamente.
  • Perdere i progressi dell'utente se esce dall'app e torna in un secondo momento.
  • Si arresta in modo anomalo o si interrompe l'avanzamento dell'utente quando lo schermo ruota da orientamento orizzontale a verticale.

Questo documento spiega in dettaglio il ciclo di vita delle attività. Il documento inizia descrivendo il paradigma del ciclo di vita. Poi spiega ciascuno dei callback: cosa succede internamente durante l'esecuzione e cosa devi implementare durante questo processo.

Illustra quindi brevemente la relazione tra lo stato dell'attività e la vulnerabilità di un processo all'interruzione del sistema. Infine, tratta diversi argomenti relativi alle transizioni tra gli stati di attività.

Per informazioni sulla gestione dei cicli di vita, incluse indicazioni sulle best practice, consulta Gestione dei cicli di vita con componenti sensibili al ciclo di vita e Salvare gli stati dell'interfaccia utente. Per scoprire come sviluppare un'app solida e di qualità produttiva utilizzando attività in combinazione con i componenti dell'architettura, consulta la guida all'architettura delle app.

Concetti del ciclo di vita delle attività

Per esplorare le transizioni tra le fasi del ciclo di vita dell'attività, la classe Activity fornisce un set base di sei callback: onCreate(), onStart(), onResume(), onPause(), onStop() e onDestroy(). Il sistema richiama ciascuno di questi callback quando l'attività entra in un nuovo stato.

La Figura 1 presenta una rappresentazione visiva di questo paradigma.

Figura 1. Un'illustrazione semplificata del ciclo di vita dell'attività.

Quando l'utente inizia ad abbandonare l'attività, il sistema chiama dei metodi per eliminare l'attività. In alcuni casi, l'attività viene smantellata solo parzialmente e rimane ancora in memoria, ad esempio quando l'utente passa a un'altra app. In questi casi, l'attività può comunque tornare in primo piano.

Se l'utente torna all'attività, questa riprende dal punto in cui l'utente l'aveva interrotta. Con alcune eccezioni, alle app è limitato l'avvio delle attività durante l'esecuzione in background.

La probabilità del sistema di terminare un determinato processo, insieme alle attività al suo interno, dipende dallo stato dell'attività in quel momento. Per saperne di più sulla relazione tra stato e vulnerabilità all'esclusione, consulta la sezione relativa a stato dell'attività ed espulsione dalla memoria.

A seconda della complessità dell'attività, probabilmente non è necessario implementare tutti i metodi del ciclo di vita. Tuttavia, è importante che tu comprenda tutti questi criteri e implementi quelli che fanno sì che la tua app si comporti come previsto dagli utenti.

Callback del ciclo di vita

Questa sezione fornisce informazioni concettuali e di implementazione dei metodi di callback utilizzati durante il ciclo di vita dell'attività.

Alcune azioni appartengono ai metodi del ciclo di vita dell'attività. Tuttavia, inserisci nel componente codice che implementa le azioni di un componente dipendente, anziché il metodo del ciclo di vita dell'attività. Per farlo, devi rendere il componente dipendente consapevole del ciclo di vita. Per informazioni su come rendere sensibili al ciclo di vita i componenti dipendenti, consulta Gestione dei cicli di vita con componenti sensibili al ciclo di vita.

onCreate()

Devi implementare questo callback, che si attiva quando il sistema crea l'attività per la prima volta. Al momento della creazione, l'attività passa allo stato Creata. Nel metodo onCreate(), esegui la logica di avvio dell'applicazione di base che si verifica una sola volta per l'intera durata dell'attività.

Ad esempio, la tua implementazione di onCreate() potrebbe associare dati a elenchi, associare l'attività a un ViewModel e creare un'istanza di alcune variabili con ambito di classe. Questo metodo riceve il parametro savedInstanceState, che è un oggetto Bundle contenente lo stato salvato in precedenza dell'attività. Se l'attività non è mai esistita prima, il valore dell'oggetto Bundle è null.

Se hai un componente sensibile al ciclo di vita collegato al ciclo di vita della tua attività, riceve l'evento ON_CREATE. Il metodo annotato con @OnLifecycleEvent viene chiamato in modo che il componente sensibile al ciclo di vita possa eseguire qualsiasi codice di configurazione necessario per lo stato creato.

Il seguente esempio del metodo onCreate() mostra la configurazione di base dell'attività, come la dichiarazione dell'interfaccia utente (definita in un file di layout XML), la definizione delle variabili membro e la configurazione di parte dell'interfaccia utente. In questo esempio, il file di layout XML passa l'ID risorsa del file R.layout.main_activity a setContentView().

Kotlin

lateinit var textView: TextView

// Some transient state for the activity instance.
var gameState: String? = null

override fun onCreate(savedInstanceState: Bundle?) {
    // Call the superclass onCreate to complete the creation of
    // the activity, like the view hierarchy.
    super.onCreate(savedInstanceState)

    // Recover the instance state.
    gameState = savedInstanceState?.getString(GAME_STATE_KEY)

    // Set the user interface layout for this activity.
    // The layout is defined in the project res/layout/main_activity.xml file.
    setContentView(R.layout.main_activity)

    // Initialize member TextView so it is available later.
    textView = findViewById(R.id.text_view)
}

// This callback is called only when there is a saved instance previously saved using
// onSaveInstanceState(). Some state is restored in onCreate(). Other state can optionally
// be restored here, possibly usable after onStart() has completed.
// The savedInstanceState Bundle is same as the one used in onCreate().
override fun onRestoreInstanceState(savedInstanceState: Bundle?) {
    textView.text = savedInstanceState?.getString(TEXT_VIEW_KEY)
}

// Invoked when the activity might be temporarily destroyed; save the instance state here.
override fun onSaveInstanceState(outState: Bundle?) {
    outState?.run {
        putString(GAME_STATE_KEY, gameState)
        putString(TEXT_VIEW_KEY, textView.text.toString())
    }
    // Call superclass to save any view hierarchy.
    super.onSaveInstanceState(outState)
}

Java

TextView textView;

// Some transient state for the activity instance.
String gameState;

@Override
public void onCreate(Bundle savedInstanceState) {
    // Call the superclass onCreate to complete the creation of
    // the activity, like the view hierarchy.
    super.onCreate(savedInstanceState);

    // Recover the instance state.
    if (savedInstanceState != null) {
        gameState = savedInstanceState.getString(GAME_STATE_KEY);
    }

    // Set the user interface layout for this activity.
    // The layout is defined in the project res/layout/main_activity.xml file.
    setContentView(R.layout.main_activity);

    // Initialize member TextView so it is available later.
    textView = (TextView) findViewById(R.id.text_view);
}

// This callback is called only when there is a saved instance previously saved using
// onSaveInstanceState(). Some state is restored in onCreate(). Other state can optionally
// be restored here, possibly usable after onStart() has completed.
// The savedInstanceState Bundle is same as the one used in onCreate().
@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
    textView.setText(savedInstanceState.getString(TEXT_VIEW_KEY));
}

// Invoked when the activity might be temporarily destroyed; save the instance state here.
@Override
public void onSaveInstanceState(Bundle outState) {
    outState.putString(GAME_STATE_KEY, gameState);
    outState.putString(TEXT_VIEW_KEY, textView.getText());

    // Call superclass to save any view hierarchy.
    super.onSaveInstanceState(outState);
}

In alternativa alla definizione del file XML e al suo passaggio a setContentView(), puoi creare nuovi oggetti View nel codice attività e creare una gerarchia delle viste inserendo nuovi oggetti View in ViewGroup. Potrai quindi utilizzare quel layout passando il ViewGroup principale a setContentView(). Per ulteriori informazioni sulla creazione di un'interfaccia utente, consulta la documentazione relativa all'interfaccia utente.

La tua attività non rimane nello stato Creato. Al termine dell'esecuzione del metodo onCreate(), l'attività diventa Iniziata e il sistema chiama i metodi onStart() e onResume() in successione.

onStart()

Quando l'attività passa allo stato Iniziato, il sistema richiama onStart(). Questa chiamata rende l'attività visibile all'utente mentre l'app si prepara affinché entri in primo piano e diventi interattiva. Ad esempio, con questo metodo viene inizializzato il codice che gestisce l'interfaccia utente.

Quando l'attività passa allo stato Iniziato, qualsiasi componente sensibile al ciclo di vita collegato al ciclo di vita dell'attività riceve l'evento ON_START.

Il metodo onStart() viene completato rapidamente e, come per lo stato Creata, l'attività non rimane nello stato Iniziato. Al termine del callback, l'attività entra nello stato Riprendi e il sistema richiama il metodo onResume().

(())

Quando l'attività entra nello stato Ripresa, passa in primo piano e il sistema richiama il callback onResume(). Questo è lo stato in cui l'app interagisce con l'utente. L'app rimane in questo stato finché qualcosa non riesce a distogliere lo stato attivo dall'app, ad esempio quando il dispositivo riceve una telefonata, l'utente passa a un'altra attività o lo schermo del dispositivo si spegne.

Quando l'attività passa allo stato Ripresa, qualsiasi componente sensibile al ciclo di vita e associato al ciclo di vita dell'attività riceve l'evento ON_RESUME. I componenti del ciclo di vita possono attivare qualsiasi funzionalità che deve essere eseguita mentre il componente è visibile e in primo piano, ad esempio l'avvio di un'anteprima della videocamera.

Quando si verifica un evento invasivo, l'attività entra nello stato In pausa e il sistema richiama il callback onPause().

Se l'attività torna allo stato Ripresa dallo stato In pausa, il sistema richiama nuovamente il metodo onResume(). Per questo motivo, implementa onResume() per inizializzare i componenti rilasciati durante onPause() e per eseguire eventuali altre inizializzazioni che devono avvenire ogni volta che l'attività entra nello stato Ripresa.

Ecco un esempio di componente sensibile al ciclo di vita che accede alla videocamera quando il componente riceve l'evento ON_RESUME:

Kotlin

class CameraComponent : LifecycleObserver {
    ...
    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    fun initializeCamera() {
        if (camera == null) {
            getCamera()
        }
    }
    ...
}

Java

public class CameraComponent implements LifecycleObserver {

    ...

    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    public void initializeCamera() {
        if (camera == null) {
            getCamera();
        }
    }
    ...
}

Il codice precedente inizializza la videocamera una volta che LifecycleObserver riceve l'evento ON_RESUME. In modalità multi-finestra, tuttavia, la tua attività potrebbe essere completamente visibile anche quando è in stato In pausa. Ad esempio, quando l'app è in modalità multi-finestra e l'utente tocca la finestra che non contiene la tua attività, l'attività passa allo stato In pausa.

Se vuoi che la videocamera sia attiva solo quando l'app è in stato Riprendi (visibile e attiva in primo piano), inizializzala dopo l'evento ON_RESUME dimostrato in precedenza. Se vuoi mantenere la videocamera attiva mentre l'attività è In pausa ma visibile, ad esempio in modalità multi-finestra, inizializza la videocamera dopo l'evento ON_START.

Tuttavia, se la videocamera è attiva mentre l'attività è in pausa, potrebbe negare l'accesso alla videocamera a un'altra app ripresa in modalità multi-finestra. A volte è necessario tenere la videocamera attiva mentre l'attività è in pausa, ma se lo fai potrebbe in realtà peggiorare l'esperienza utente complessiva.

Per questo motivo, valuta attentamente in che punto del ciclo di vita è più appropriato assumere il controllo delle risorse di sistema condivise nel contesto della modalità multi-finestra. Per scoprire di più sul supporto della modalità multi-finestra, consulta Supporto multi-finestra.

Indipendentemente dall'evento di build in cui scegli di eseguire un'operazione di inizializzazione, assicurati di utilizzare l'evento del ciclo di vita corrispondente per rilasciare la risorsa. Se inizializzi un elemento dopo l'evento ON_START, rilascialo o interrompilo dopo l'evento ON_STOP. Se iniziali dopo l'evento ON_RESUME, rilascia dopo l'evento ON_PAUSE.

Lo snippet di codice precedente inserisce il codice di inizializzazione della fotocamera in un componente sensibile al ciclo di vita. Puoi inserire questo codice direttamente nei callback del ciclo di vita delle attività, come onStart() e onStop(), ma ti sconsigliamo di farlo. L'aggiunta di questa logica a un componente indipendente sensibile al ciclo di vita ti consente di riutilizzare il componente in più attività senza dover duplicare il codice. Per informazioni su come creare un componente sensibile al ciclo di vita, consulta Gestione dei cicli di vita con componenti sensibili al ciclo di vita.

onPause()

Il sistema chiama questo metodo come prima indicazione che l'utente sta abbandonando l'attività, anche se ciò non significa sempre che l'attività sia stata eliminata. Indica che l'attività non è più in primo piano, ma è ancora visibile se l'utente è in modalità multi-finestra. Esistono diversi motivi per cui un'attività può entrare in questo stato:

  • Un evento che interrompe l'esecuzione dell'app, come descritto nella sezione sul callback onRiprendi(), mette in pausa l'attività corrente. Questo è il caso più comune.
  • Nella modalità multi-finestra, è attiva una sola app alla volta e il sistema mette in pausa tutte le altre.
  • L'apertura di una nuova attività semitrasparente, come una finestra di dialogo, mette in pausa l'attività coperta. Finché l'attività è parzialmente visibile ma non in stato attivo, rimane in pausa.

Quando un'attività passa allo stato In pausa, qualsiasi componente sensibile al ciclo di vita e associato al ciclo di vita dell'attività riceve l'evento ON_PAUSE. I componenti del ciclo di vita possono interrompere qualsiasi funzionalità che non deve essere eseguita mentre il componente non è in primo piano, come l'interruzione di un'anteprima della videocamera.

Utilizza il metodo onPause() per mettere in pausa o modificare le operazioni che non possono continuare o che potrebbero continuare con la moderazione, mentre Activity è in stato In pausa e che prevedi di riprendere a breve.

Puoi anche utilizzare il metodo onPause() per rilasciare risorse di sistema, handle ai sensori (come il GPS) o qualsiasi risorsa che influisce sulla durata della batteria mentre l'attività è in pausa e non è necessaria all'utente.

Tuttavia, come menzionato nella sezione su onResume(), un'attività in pausa potrebbe essere comunque completamente visibile se l'app è in modalità multi-finestra. Potresti usare onStop() anziché onPause() per rilasciare o modificare completamente le risorse e le operazioni relative all'interfaccia utente in modo da supportare meglio la modalità multi-finestra.

L'esempio seguente di una reazione LifecycleObserver all'evento ON_PAUSE è la controparte dell'esempio di evento ON_RESUME precedente, rilasciando la videocamera che viene inizializzata dopo la ricezione dell'evento ON_RESUME:

Kotlin

class CameraComponent : LifecycleObserver {
    ...
    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    fun releaseCamera() {
        camera?.release()
        camera = null
    }
    ...
}

Java

public class JavaCameraComponent implements LifecycleObserver {
    ...
    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    public void releaseCamera() {
        if (camera != null) {
            camera.release();
            camera = null;
        }
    }
    ...
}

In questo esempio viene inserito il codice di rilascio della videocamera dopo che l'evento ON_PAUSE è stato ricevuto da LifecycleObserver.

L'esecuzione di onPause() è molto breve e non offre necessariamente tempo sufficiente per eseguire operazioni di salvataggio. Per questo motivo, non utilizzare onPause() per salvare i dati utente o delle applicazioni, effettuare chiamate di rete o eseguire transazioni del database. Questa operazione potrebbe non essere completata prima del completamento del metodo.

Esegui invece operazioni di arresto con carico elevato durante onStop(). Per saperne di più sulle operazioni adatte da eseguire durante onStop(), consulta la sezione successiva. Per ulteriori informazioni sul salvataggio dei dati, consulta la sezione Salvataggio e ripristino dello stato.

Il completamento del metodo onPause() non significa che l'attività lascia lo stato In pausa. Rimane invece in questo stato finché non viene ripresa o fino a quando non diventa completamente invisibile per l'utente. Se l'attività riprende, il sistema richiama nuovamente il callback onResume().

Se l'attività torna dallo stato In pausa allo stato Ripresa, il sistema mantiene l'istanza Activity residente in memoria, richiamando l'istanza quando il sistema richiama onResume(). In questo scenario, non è necessario reinizializzare i componenti creati durante i metodi di callback che portano allo stato Ripristinato. Se l'attività diventa completamente invisibile, il sistema chiama onStop().

onStop()

Quando l'attività non è più visibile all'utente, viene attivata lo stato Arrestata e il sistema richiama il callback onStop(). Questo può verificarsi quando un'attività appena avviata copre l'intero schermo. Il sistema chiama onStop() anche quando termina l'attività e sta per essere terminata.

Quando l'attività passa allo stato Interrotto, qualsiasi componente sensibile al ciclo di vita e associato al ciclo di vita dell'attività riceve l'evento ON_STOP. Qui i componenti del ciclo di vita possono interrompere qualsiasi funzionalità che non deve essere eseguita mentre il componente non è visibile sullo schermo.

Nel metodo onStop(), rilascia o modifica le risorse che non sono necessarie quando l'app non è visibile all'utente. Ad esempio, l'app potrebbe mettere in pausa le animazioni o passare dagli aggiornamenti della posizione granulari a quelli granulari. Se utilizzi onStop() anziché onPause(), il lavoro relativo all'interfaccia utente continua anche quando l'utente visualizza la tua attività in modalità multi-finestra.

Inoltre, usa onStop() per eseguire operazioni di arresto che consumano molta CPU. Ad esempio, se non riesci a trovare un momento migliore per salvare le informazioni in un database, puoi farlo durante onStop(). L'esempio seguente mostra un'implementazione di onStop() che salva i contenuti di una bozza di nota nell'archiviazione permanente:

Kotlin

override fun onStop() {
    // Call the superclass method first.
    super.onStop()

    // Save the note's current draft, because the activity is stopping
    // and we want to be sure the current note progress isn't lost.
    val values = ContentValues().apply {
        put(NotePad.Notes.COLUMN_NAME_NOTE, getCurrentNoteText())
        put(NotePad.Notes.COLUMN_NAME_TITLE, getCurrentNoteTitle())
    }

    // Do this update in background on an AsyncQueryHandler or equivalent.
    asyncQueryHandler.startUpdate(
            token,     // int token to correlate calls
            null,      // cookie, not used here
            uri,       // The URI for the note to update.
            values,    // The map of column names and new values to apply to them.
            null,      // No SELECT criteria are used.
            null       // No WHERE columns are used.
    )
}

Java

@Override
protected void onStop() {
    // Call the superclass method first.
    super.onStop();

    // Save the note's current draft, because the activity is stopping
    // and we want to be sure the current note progress isn't lost.
    ContentValues values = new ContentValues();
    values.put(NotePad.Notes.COLUMN_NAME_NOTE, getCurrentNoteText());
    values.put(NotePad.Notes.COLUMN_NAME_TITLE, getCurrentNoteTitle());

    // Do this update in background on an AsyncQueryHandler or equivalent.
    asyncQueryHandler.startUpdate (
            mToken,  // int token to correlate calls
            null,    // cookie, not used here
            uri,    // The URI for the note to update.
            values,  // The map of column names and new values to apply to them.
            null,    // No SELECT criteria are used.
            null     // No WHERE columns are used.
    );
}

L'esempio di codice precedente utilizza direttamente SQLite. Comunque, consigliamo di usare Room, una libreria di persistenza che fornisce un livello di astrazione su SQLite. Per ulteriori informazioni sui vantaggi dell'utilizzo di Room e su come implementarlo nella tua app, consulta la guida Room Persistence Library.

Quando l'attività entra nello stato Interrotto, l'oggetto Activity viene mantenuto residente in memoria: mantiene tutte le informazioni su stato e membro, ma non viene collegato al gestore di finestre. Quando l'attività riprende, le informazioni vengono recuperate.

Non è necessario reinizializzare i componenti creati durante i metodi di callback che portano allo stato Ripreso. Il sistema tiene anche traccia dello stato corrente di ogni oggetto View nel layout, pertanto se l'utente inserisce del testo in un widget EditText, quel contenuto viene conservato in modo da non essere necessario salvarlo e ripristinarlo.

Nota: una volta interrotta l'attività, il sistema potrebbe eliminare il processo che contiene l'attività se deve recuperare memoria. Anche se distrugge il processo mentre l'attività è interrotta, mantiene comunque lo stato degli oggetti View, ad esempio il testo in un widget EditText, in un Bundle (un blob di coppie chiave-valore) e li ripristina quando l'utente torna all'attività. Per ulteriori informazioni sul ripristino di un'attività a cui l'utente torna, consulta la sezione relativa al salvataggio e al ripristino dello stato.

Nello stato Interrotto, l'attività torna a interagire con l'utente oppure l'attività è terminata e scompare. Se l'attività si ripresenta, il sistema richiama onRestart(). Se Activity non è più in esecuzione, il sistema chiama onDestroy().

onDestroy()

onDestroy() viene chiamato prima dell'eliminazione dell'attività. Il sistema richiama questo callback per uno dei due motivi seguenti:

  1. L'attività è in fase di completamento perché l'utente ha ignorato completamente l'attività o perché finish() è stato chiamato per l'attività.
  2. Il sistema sta eliminando temporaneamente l'attività a causa di una modifica alla configurazione, ad esempio la rotazione del dispositivo o l'attivazione della modalità multi-finestra.

Quando l'attività passa allo stato Eliminato, qualsiasi componente sensibile al ciclo di vita e associato al ciclo di vita dell'attività riceve l'evento ON_DESTROY. È qui che i componenti del ciclo di vita possono eliminare tutto ciò di cui hanno bisogno prima di eliminare Activity.

Anziché inserire la logica in Activity per determinare il motivo dell'eliminazione, utilizza un oggetto ViewModel per contenere i dati di visualizzazione pertinenti per Activity. Se Activity viene ricreato a causa di una modifica alla configurazione, ViewModel non deve fare nulla, poiché viene conservato e assegnato alla successiva istanza Activity.

Se l'elemento Activity non viene ricreato, in ViewModel viene chiamato il metodo onCleared(), che consente di ripulire tutti i dati necessari prima di essere eliminati. Puoi distinguere tra questi due scenari con il metodo isFinishing().

Se l'attività è in fase di completamento, onDestroy() è il callback finale del ciclo di vita ricevuto dall'attività. Se onDestroy() viene richiamato in seguito a una modifica alla configurazione, il sistema crea immediatamente una nuova istanza di attività e poi richiama onCreate() su quella nuova istanza nella nuova configurazione.

Il callback onDestroy() rilascia tutte le risorse non svincolate dai callback precedenti, ad esempio onStop().

Stato dell'attività ed espulsione dalla memoria

Il sistema termina i processi quando ha bisogno di liberare RAM. La probabilità che il sistema interrompa un determinato processo dipende dallo stato del processo in quel momento. Lo stato del processo dipende a sua volta dallo stato dell'attività in esecuzione al suo interno. La tabella 1 mostra le correlazioni tra stato del processo, stato dell'attività e probabilità che il sistema termini il processo. Questa tabella si applica solo se un processo non esegue altri tipi di componenti dell'applicazione.

Probabilità di uccisioni Stato processo Stato dell'attività finale
Minimo Primo piano (in primo piano o in procinto di concentrarsi) Ripristinato
Bassa Visibile (nessuno stato attivo) Iniziata/In pausa
Superiore Sfondo (invisibile) Interrotto
Massimo Vuoto Eliminata

Tabella 1. Relazione tra ciclo di vita del processo e stato dell'attività.

Il sistema non interrompe mai direttamente un'attività per liberare memoria. Al contrario, interrompe il processo in cui viene eseguita l'attività, distruggendo non solo l'attività, ma anche tutto il resto in esecuzione nel processo. Per informazioni su come preservare e ripristinare lo stato dell'interfaccia utente dell'attività quando si verifica un decesso del processo avviato dal sistema, consulta la sezione relativa al salvataggio e al ripristino dello stato.

L'utente può anche terminare un processo utilizzando Gestione applicazioni, in Impostazioni, per terminare l'app corrispondente.

Per ulteriori informazioni sui processi, consulta la panoramica dei processi e dei thread.

Salvataggio e ripristino dello stato temporaneo dell'interfaccia utente

Un utente si aspetta che lo stato UI di un'attività rimanga invariato durante una modifica alla configurazione, ad esempio la rotazione o il passaggio alla modalità multi-finestra. Tuttavia, quando si verifica una modifica della configurazione, il sistema elimina l'attività per impostazione predefinita, cancellando qualsiasi stato della UI archiviato nell'istanza dell'attività.

Analogamente, un utente si aspetta che lo stato dell'interfaccia utente rimanga lo stesso se passa temporaneamente dalla tua app a un'altra app e poi torna all'app in un secondo momento. Tuttavia, il sistema può eliminare il processo dell'applicazione quando l'utente è assente e la tua attività viene interrotta.

Quando i vincoli di sistema eliminano l'attività, preserva lo stato temporaneo dell'interfaccia utente utilizzando una combinazione di ViewModel, onSaveInstanceState() e/o spazio di archiviazione locale. Per scoprire di più sulle aspettative degli utenti rispetto al comportamento del sistema e su come conservare al meglio i dati complessi sullo stato dell'interfaccia utente nelle attività avviate dal sistema e in caso di interruzione del processo, consulta Salvare gli stati dell'interfaccia utente.

Questa sezione illustra lo stato dell'istanza e come implementare il metodo onSaveInstance(), che è un callback sull'attività stessa. Se i dati UI sono leggeri, puoi utilizzare solo onSaveInstance() per mantenere lo stato dell'interfaccia utente sia in caso di modifiche alla configurazione sia in caso di interruzione del processo avviato dal sistema. Tuttavia, poiché onSaveInstance() comporta costi di serializzazione/deserializzazione, nella maggior parte dei casi si utilizzano sia ViewModel che onSaveInstance(), come descritto in Salva gli stati dell'interfaccia utente.

Nota: per scoprire di più sulle modifiche alla configurazione, su come limitare la ricreazione delle attività se necessario e su come reagire a queste modifiche alla configurazione dalla visualizzazione del sistema e da Jetpack Compose, consulta la pagina Gestire le modifiche alla configurazione.

Stato istanza

Esistono alcuni scenari in cui l'attività viene eliminata a causa del normale comportamento dell'app, ad esempio quando l'utente preme il pulsante Indietro o l'attività segnala la propria distruzione chiamando il metodo finish().

Quando l'attività viene eliminata perché l'utente preme Indietro o perché l'attività si conclude da sola, il concetto di questa istanza Activity sia del sistema sia dell'utente perde per sempre. In questi scenari, l'aspettativa dell'utente corrisponde al comportamento del sistema e tu non devi fare altro.

Tuttavia, se il sistema elimina l'attività a causa di vincoli del sistema (ad esempio una modifica della configurazione o una pressione della memoria), anche se l'istanza Activity effettiva è stata eliminata, ricorda che esisteva. Se l'utente tenta di tornare all'attività, il sistema crea una nuova istanza di tale attività utilizzando un set di dati salvati che descrive lo stato dell'attività al momento dell'eliminazione.

I dati salvati che il sistema utilizza per ripristinare lo stato precedente vengono chiamati stato istanza. È una raccolta di coppie chiave-valore archiviate in un oggetto Bundle. Per impostazione predefinita, il sistema utilizza lo stato dell'istanza Bundle per salvare informazioni su ogni oggetto View nel layout delle attività, ad esempio il valore di testo inserito in un widget EditText.

Quindi, se l'istanza dell'attività viene eliminata e ricreata, lo stato del layout viene ripristinato allo stato precedente senza necessità di codice. Tuttavia, l'attività potrebbe avere più informazioni sullo stato che vuoi ripristinare, ad esempio variabili membro che monitorano l'avanzamento dell'utente nell'attività.

Nota: affinché il sistema Android ripristini lo stato delle viste nell'attività, ogni vista deve avere un ID univoco, fornito dall'attributo android:id.

Un oggetto Bundle non è appropriato per conservare più di una quantità banale di dati, perché richiede la serializzazione nel thread principale e consuma memoria di processo di sistema. Per conservare più di una quantità molto ridotta di dati, adotta un approccio combinato alla conservazione dei dati, utilizzando l'archiviazione locale permanente, il metodo onSaveInstanceState() e la classe ViewModel, come descritto in Salvare gli stati dell'interfaccia utente.

Salva lo stato dell'interfaccia utente semplice e leggero utilizzando onSaveInstanceState()

Quando l'attività inizia a interrompersi, il sistema chiama il metodo onSaveInstanceState() in modo che l'attività possa salvare le informazioni sullo stato in un bundle di stato dell'istanza. L'implementazione predefinita di questo metodo salva le informazioni temporanee sullo stato della gerarchia delle visualizzazioni dell'attività, ad esempio il testo in un widget EditText o la posizione di scorrimento di un widget ListView.

Per salvare ulteriori informazioni sullo stato delle istanze per l'attività, esegui l'override di onSaveInstanceState() e aggiungi coppie chiave-valore all'oggetto Bundle salvato nell'evento in cui l'attività viene eliminata in modo imprevisto. Quando esegui l'override di onSaveInstanceState(), devi chiamare l'implementazione della superclasse se vuoi che l'implementazione predefinita salvi lo stato della gerarchia delle viste. Questo è quanto mostrato nell'esempio seguente:

Kotlin

override fun onSaveInstanceState(outState: Bundle?) {
    // Save the user's current game state.
    outState?.run {
        putInt(STATE_SCORE, currentScore)
        putInt(STATE_LEVEL, currentLevel)
    }

    // Always call the superclass so it can save the view hierarchy state.
    super.onSaveInstanceState(outState)
}

companion object {
    val STATE_SCORE = "playerScore"
    val STATE_LEVEL = "playerLevel"
}

Java

static final String STATE_SCORE = "playerScore";
static final String STATE_LEVEL = "playerLevel";
// ...


@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
    // Save the user's current game state.
    savedInstanceState.putInt(STATE_SCORE, currentScore);
    savedInstanceState.putInt(STATE_LEVEL, currentLevel);

    // Always call the superclass so it can save the view hierarchy state.
    super.onSaveInstanceState(savedInstanceState);
}

Nota: onSaveInstanceState() non viene chiamato quando l'utente chiude esplicitamente l'attività o, in altri casi, quando viene chiamato finish().

Per salvare dati permanenti, come le preferenze degli utenti o i dati per un database, cogli le opportunità appropriate quando la tua attività è in primo piano. Se non si presenta questa opportunità, salva i dati persistenti durante il metodo onStop().

Ripristina lo stato UI dell'attività utilizzando lo stato dell'istanza salvata

Quando l'attività viene ricreata dopo essere stata eliminata in precedenza, puoi recuperare lo stato dell'istanza salvata dalla Bundle che il sistema passa all'attività. Entrambi i metodi di callback onCreate() e onRestoreInstanceState() ricevono lo stesso Bundle contenente le informazioni sullo stato dell'istanza.

Poiché il metodo onCreate() viene chiamato se il sistema sta creando una nuova istanza dell'attività o ricreando una precedente, devi verificare se lo stato Bundle è nullo prima di tentare di leggerlo. Se è null, il sistema crea una nuova istanza dell'attività, anziché ripristinare una precedente eliminata.

Il seguente snippet di codice mostra come ripristinare alcuni dati di stato in onCreate():

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState) // Always call the superclass first

    // Check whether we're recreating a previously destroyed instance.
    if (savedInstanceState != null) {
        with(savedInstanceState) {
            // Restore value of members from saved state.
            currentScore = getInt(STATE_SCORE)
            currentLevel = getInt(STATE_LEVEL)
        }
    } else {
        // Probably initialize members with default values for a new instance.
    }
    // ...
}

Java

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState); // Always call the superclass first

    // Check whether we're recreating a previously destroyed instance.
    if (savedInstanceState != null) {
        // Restore value of members from saved state.
        currentScore = savedInstanceState.getInt(STATE_SCORE);
        currentLevel = savedInstanceState.getInt(STATE_LEVEL);
    } else {
        // Probably initialize members with default values for a new instance.
    }
    // ...
}

Anziché ripristinare lo stato durante onCreate(), puoi scegliere di implementare onRestoreInstanceState(), che il sistema chiama dopo il metodo onStart(). Il sistema chiama onRestoreInstanceState() solo se è presente uno stato salvato da ripristinare, quindi non è necessario verificare se Bundle sia nullo.

Kotlin

override fun onRestoreInstanceState(savedInstanceState: Bundle?) {
    // Always call the superclass so it can restore the view hierarchy.
    super.onRestoreInstanceState(savedInstanceState)

    // Restore state members from saved instance.
    savedInstanceState?.run {
        currentScore = getInt(STATE_SCORE)
        currentLevel = getInt(STATE_LEVEL)
    }
}

Java

public void onRestoreInstanceState(Bundle savedInstanceState) {
    // Always call the superclass so it can restore the view hierarchy.
    super.onRestoreInstanceState(savedInstanceState);

    // Restore state members from saved instance.
    currentScore = savedInstanceState.getInt(STATE_SCORE);
    currentLevel = savedInstanceState.getInt(STATE_LEVEL);
}

Attenzione: chiama sempre l'implementazione della superclasse di onRestoreInstanceState() in modo che l'implementazione predefinita possa ripristinare lo stato della gerarchia delle visualizzazioni.

Navigazione tra le attività

È probabile che un'app entri e esca da un'attività, magari molte volte, durante il ciclo di vita dell'app, ad esempio quando l'utente tocca il pulsante Indietro del dispositivo o quando l'attività avvia un'altra attività.

Questa sezione tratta gli argomenti che devi conoscere per implementare correttamente transizioni delle attività. Questi argomenti includono l'avvio di un'attività da un'altra, il salvataggio dello stato dell'attività e il ripristino dello stato dell'attività.

Avviare un'attività da un'altra

Spesso un'attività deve iniziare un'altra attività prima o poi. Questa necessità si presenta, ad esempio, quando un'app deve passare dalla schermata corrente a una nuova.

A seconda che l'attività richieda o meno un risultato dalla nuova attività in procinto di iniziare, puoi avviare la nuova attività utilizzando il metodo startActivity() o startActivityForResult(). In entrambi i casi, passi un oggetto Intent.

L'oggetto Intent specifica l'attività esatta che vuoi avviare o descrive il tipo di azione che vuoi eseguire. Il sistema seleziona l'attività appropriata per te, che può anche provenire da un'altra applicazione. Un oggetto Intent può trasferire anche piccole quantità di dati che verranno utilizzati dall'attività avviata. Per ulteriori informazioni sulla classe Intent, consulta Intent e filtri di intent.

funzione startActivity()

Se l'attività appena iniziata non deve restituire un risultato, l'attività attuale può avviarla chiamando il metodo startActivity().

Quando lavori all'interno di una tua applicazione, spesso devi semplicemente avviare un'attività nota. Ad esempio, il seguente snippet di codice mostra come avviare un'attività denominata SignInActivity.

Kotlin

val intent = Intent(this, SignInActivity::class.java)
startActivity(intent)

Java

Intent intent = new Intent(this, SignInActivity.class);
startActivity(intent);

L'applicazione potrebbe anche voler eseguire alcune azioni, come l'invio di un'email, un messaggio o un aggiornamento di stato, utilizzando i dati della tua attività. In questo caso, l'applicazione potrebbe non avere attività proprie per eseguire queste azioni, quindi puoi utilizzare le attività fornite da altre applicazioni sul dispositivo, che possono eseguire le azioni per tuo conto.

È qui che le intenzioni diventano davvero preziose. Puoi creare un intent che descriva un'azione da eseguire e il sistema avvia l'attività appropriata da un'altra applicazione. Se ci sono più attività in grado di gestire l'intent, l'utente può scegliere quale utilizzare. Ad esempio, se vuoi consentire all'utente di inviare un messaggio email, puoi creare il seguente intent:

Kotlin

val intent = Intent(Intent.ACTION_SEND).apply {
    putExtra(Intent.EXTRA_EMAIL, recipientArray)
}
startActivity(intent)

Java

Intent intent = new Intent(Intent.ACTION_SEND);
intent.putExtra(Intent.EXTRA_EMAIL, recipientArray);
startActivity(intent);

L'elemento aggiuntivo EXTRA_EMAIL aggiunto all'intent è un array di stringhe di indirizzi email a cui deve essere inviata l'email. Quando un'applicazione email risponde a questo intent, legge l'array di stringhe fornito nella parte aggiuntiva e inserisce gli indirizzi nel campo "a" del modulo di composizione dell'email. In questa situazione, l'attività dell'applicazione email inizia e, quando l'utente ha finito, l'attività riprende.

startActivityForResult()

A volte potresti voler ottenere il risultato di un'attività terminata. Ad esempio, potresti avviare un'attività che consenta all'utente di scegliere una persona da un elenco di contatti. Al termine, restituisce la persona selezionata. Per farlo, devi chiamare il metodo startActivityForResult(Intent, int), in cui il parametro numero intero identifica la chiamata.

Questo identificatore ha lo scopo di distinguere tra più chiamate a startActivityForResult(Intent, int) dalla stessa attività. Non è un identificatore globale e non rischia di entrare in conflitto con altre app o attività. Il risultato viene visualizzato tramite il tuo onActivityResult(int, int, Intent) metodo.

Quando un'attività secondaria termina, può chiamare setResult(int) per restituire i dati all'attività principale. L'attività secondaria deve fornire un codice risultato, che può essere i risultati standard RESULT_CANCELED, RESULT_OK o qualsiasi valore personalizzato che inizia con RESULT_FIRST_USER.

Inoltre, l'attività secondaria può restituire facoltativamente un oggetto Intent contenente eventuali dati aggiuntivi. L'attività padre utilizza il metodo onActivityResult(int, int, Intent), insieme all'identificatore numero intero fornito in origine dall'attività padre, per ricevere le informazioni.

Se un'attività secondaria non riesce per qualsiasi motivo, ad esempio un arresto anomalo, l'attività principale riceve un risultato con il codice RESULT_CANCELED.

Kotlin

class MyActivity : Activity() {
    // ...

    override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
        if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
            // When the user center presses, let them pick a contact.
            startActivityForResult(
                    Intent(Intent.ACTION_PICK,Uri.parse("content://contacts")),
                    PICK_CONTACT_REQUEST)
            return true
        }
        return false
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, intent: Intent?) {
        when (requestCode) {
            PICK_CONTACT_REQUEST ->
                if (resultCode == RESULT_OK) {
                    // A contact was picked. Display it to the user.
                    startActivity(Intent(Intent.ACTION_VIEW, intent?.data))
                }
        }
    }

    companion object {
        internal val PICK_CONTACT_REQUEST = 0
    }
}

Java

public class MyActivity extends Activity {
     // ...

     static final int PICK_CONTACT_REQUEST = 0;

     public boolean onKeyDown(int keyCode, KeyEvent event) {
         if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
             // When the user center presses, let them pick a contact.
             startActivityForResult(
                 new Intent(Intent.ACTION_PICK,
                 new Uri("content://contacts")),
                 PICK_CONTACT_REQUEST);
            return true;
         }
         return false;
     }

     protected void onActivityResult(int requestCode, int resultCode,
             Intent data) {
         if (requestCode == PICK_CONTACT_REQUEST) {
             if (resultCode == RESULT_OK) {
                 // A contact was picked. Display it to the user.
                 startActivity(new Intent(Intent.ACTION_VIEW, data));
             }
         }
     }
 }

Coordinare le attività

Quando un'attività ne inizia un'altra, entrambe sperimentano le transizioni del ciclo di vita. La prima attività smette di funzionare ed entra nello stato In pausa o Arrestata, mentre viene creata l'altra attività. Se queste attività condividono dati salvati sul disco o altrove, è importante capire che la prima attività non viene interrotta completamente prima della creazione della seconda. Piuttosto, il processo di avvio del secondo evento si sovrappone a quello di interruzione della prima.

L'ordine dei callback del ciclo di vita è ben definito, in particolare quando le due attività si trovano nello stesso processo, ovvero nella stessa app, e una avvia l'altra. Ecco l'ordine delle operazioni che si verificano quando l'attività A avvia l'attività B:

  1. Viene eseguito il metodo onPause() dell'attività A.
  2. I metodi onCreate(), onStart() e onResume() dell'attività B vengono eseguiti in sequenza. L'attività B ora è incentrata sull'utente.
  3. Se l'Attività A non è più visibile sullo schermo, viene eseguito il metodo onStop().

Questa sequenza di callback del ciclo di vita ti consente di gestire la transizione delle informazioni da un'attività a un'altra.