Gestire le modifiche alla configurazione

Alcune configurazioni del dispositivo possono cambiare mentre l'app è in esecuzione. Questi includono, a titolo esemplificativo:

  • Dimensioni di visualizzazione dell'app
  • Orientamento dello schermo
  • Dimensioni e spessore del carattere
  • Impostazioni internazionali
  • Confronto tra modalità Buio e modalità Luce
  • Disponibilità della tastiera

La maggior parte di queste modifiche alla configurazione è dovuta a una certa interazione dell'utente. Ad esempio, la rotazione o la chiusura del dispositivo comporta la modifica della quantità di spazio sullo schermo disponibile per l'app. Analogamente, la modifica di impostazioni del dispositivo quali dimensioni del carattere, lingua o tema preferito cambia i rispettivi valori nell'oggetto Configuration.

Questi parametri richiedono in genere modifiche all'interfaccia utente dell'applicazione sufficientemente grandi da far sì che la piattaforma Android disponga di un meccanismo creato appositamente per le modifiche. Questo meccanismo è Activity ricreazione.

Attività ricreative

Il sistema ricrea un Activity quando si verifica una modifica della configurazione. Per farlo, il sistema chiama onDestroy() ed elimina l'istanza Activity esistente. Viene quindi creata una nuova istanza utilizzando onCreate() e la nuova istanza Activity viene inizializzata con la nuova configurazione aggiornata. Inoltre, il sistema ricrea l'UI con la nuova configurazione.

Il comportamento di ricreazione consente all'applicazione di adattarsi a nuove configurazioni ricaricando automaticamente l'applicazione con risorse alternative che corrispondono alla nuova configurazione del dispositivo.

Esempio di attività ricreative

Considera un TextView che mostra un titolo statico utilizzando android:text="@string/title", come definito in un file XML di layout. Quando viene creata, la visualizzazione imposta il testo esattamente una volta, in base alla lingua corrente. Se la lingua cambia, il sistema ricrea l'attività. Di conseguenza, il sistema ricrea anche la visualizzazione e la inizializza al valore corretto in base al nuovo linguaggio.

La ricreazione cancella anche qualsiasi stato mantenuto come campi in Activity o in uno qualsiasi dei Fragment, View o altri oggetti contenuti. Il motivo è che la ricreazione di Activity crea un'istanza completamente nuova di Activity e della UI. Inoltre, il vecchio Activity non è più visibile o valido, quindi tutti i riferimenti rimanenti a questo oggetto o agli oggetti che contiene sono obsoleti. Possono causare bug, perdite di memoria e arresti anomali.

Aspettative degli utenti

L'utente di un'app si aspetta che lo stato venga conservato. Se un utente compila un modulo e apre un'altra app in modalità multi-finestra per fare riferimento alle informazioni, l'esperienza utente potrebbe essere negativa se torna a un modulo cancellato o a un'altra parte dell'app. In qualità di sviluppatore, devi fornire un'esperienza utente coerente tramite modifiche alla configurazione e la ricreazione dell'attività.

Per verificare se lo stato viene mantenuto nell'applicazione, puoi eseguire azioni che causano modifiche alla configurazione sia quando l'app è in primo piano sia quando è in background. Queste azioni includono:

  • Rotazione del dispositivo
  • Attivazione della modalità multi-finestra in corso...
  • Ridimensionamento dell'applicazione in modalità multi-finestra o in formato libero
  • Piegare un dispositivo pieghevole con più display
  • Modifica del tema di sistema, ad esempio modalità Buio o modalità Luce
  • Modifica delle dimensioni del carattere
  • Modificare la lingua del sistema o dell'app
  • Collegare o scollegare una tastiera hardware
  • Collegare o scollegare un dock

Esistono tre approcci principali che puoi adottare per preservare lo stato pertinente attraverso la ricreazione di Activity. Quale utilizzare dipende dal tipo di stato che vuoi conservare:

  • Persistenza locale per gestire la morte dei processi per dati complessi o di grandi dimensioni. L'archiviazione locale permanente include database o DataStore.
  • Oggetti conservati, come le istanze ViewModel, per gestire lo stato relativo all'UI in memoria mentre l'utente utilizza attivamente l'app.
  • Stato dell'istanza salvato per gestire la morte del processo avviato dal sistema e mantenere lo stato temporaneo che dipende dall'input dell'utente o dalla navigazione.

Per ulteriori informazioni sulle API disponibili per ciascuna di queste API in dettaglio e per il loro utilizzo appropriato, consulta Salvare gli stati dell'interfaccia utente.

Limita ricreazione attività

Puoi impedire la ricreazione automatica dell'attività per determinate modifiche alla configurazione. La ricreazione di Activity comporta la nuova creazione dell'intera UI e di tutti gli oggetti derivati da Activity. Potresti avere buoni motivi per evitarlo. Ad esempio, la tua app potrebbe non dover aggiornare le risorse durante una modifica specifica della configurazione o potresti avere una limitazione delle prestazioni. In questo caso, puoi dichiarare che la tua attività gestisce la modifica della configurazione e impedire al sistema di riavviarla.

Per disabilitare la ricreazione dell'attività per determinate modifiche alla configurazione, aggiungi il tipo di configurazione a android:configChanges nella voce <activity> del file AndroidManifest.xml. I valori possibili sono riportati nella documentazione dell'attributo android:configChanges.

Il seguente codice manifest disattiva la ricreazione di Activity per MyActivity quando l'orientamento dello schermo e la disponibilità della tastiera cambiano:

<activity
    android:name=".MyActivity"
    android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"
    android:label="@string/app_name">

Alcune modifiche alla configurazione causano sempre il riavvio dell'attività. Non puoi disabilitarle. Ad esempio, non puoi disattivare la cambio di colori dinamici introdotta in Android 12L (livello API 32).

Reagisci alle modifiche alla configurazione nel sistema Visualizza

Nel sistema View, quando viene apportata una modifica alla configurazione per la quale hai disabilitato Activity ricreazione, l'attività riceve una chiamata a Activity.onConfigurationChanged(). Tutte le viste allegate ricevono anche una chiamata a View.onConfigurationChanged(). Per le modifiche alla configurazione che non hai aggiunto a android:configChanges, il sistema ricrea l'attività come di consueto.

Il metodo di callback onConfigurationChanged() riceve un oggetto Configuration che specifica la nuova configurazione del dispositivo. Leggi i campi nell'oggetto Configuration per determinare la nuova configurazione. Per apportare le modifiche successive, aggiorna le risorse che utilizzi nell'interfaccia. Quando il sistema chiama questo metodo, l'oggetto Resources dell'attività viene aggiornato in modo da restituire risorse in base alla nuova configurazione. In questo modo puoi reimpostare elementi dell'interfaccia utente senza che il sistema riavvii la tua attività.

Ad esempio, la seguente implementazione di onConfigurationChanged() verifica se sia disponibile una tastiera:

Kotlin

override fun onConfigurationChanged(newConfig: Configuration) {
    super.onConfigurationChanged(newConfig)

    // Checks whether a keyboard is available
    if (newConfig.keyboardHidden === Configuration.KEYBOARDHIDDEN_YES) {
        Toast.makeText(this, "Keyboard available", Toast.LENGTH_SHORT).show()
    } else if (newConfig.keyboardHidden === Configuration.KEYBOARDHIDDEN_NO) {
        Toast.makeText(this, "No keyboard", Toast.LENGTH_SHORT).show()
    }
}

Java

@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);

    // Checks whether a keyboard is available
    if (newConfig.keyboardHidden == Configuration.KEYBOARDHIDDEN_YES) {
        Toast.makeText(this, "Keyboard available", Toast.LENGTH_SHORT).show();
    } else if (newConfig.keyboardHidden == Configuration.KEYBOARDHIDDEN_NO){
        Toast.makeText(this, "No keyboard", Toast.LENGTH_SHORT).show();
    }
}

Se non devi aggiornare l'applicazione in base a queste modifiche alla configurazione, puoi non implementare onConfigurationChanged(). In questo caso, tutte le risorse utilizzate prima della modifica della configurazione vengono ancora utilizzate e hai solo evitato il riavvio dell'attività. Ad esempio, un'app TV potrebbe non voler reagire quando una tastiera Bluetooth è collegata o scollegata.

Stato di conservazione

Quando utilizzi questa tecnica, devi comunque mantenere lo stato durante il normale ciclo di vita dell'attività. Ciò è dovuto ai seguenti motivi:

  • Modifiche non evitabili: modifiche alla configurazione che non puoi impedire possono riavviare l'applicazione.
  • Decesso dei processi: l'applicazione deve essere in grado di gestire l'interruzione dei processi avviati dal sistema. Se l'utente abbandona l'applicazione e l'app passa in background, il sistema potrebbe distruggerla.

Reagisci alle modifiche alla configurazione in Jetpack Compose

Jetpack Compose consente alla tua app di reagire più facilmente alle modifiche alla configurazione. Tuttavia, se disattivi la ricreazione di Activity per tutte le modifiche alla configurazione in cui è possibile farlo, l'app deve comunque gestire correttamente le modifiche alla configurazione.

L'oggetto Configuration è disponibile nella gerarchia dell'interfaccia utente di Compose con la composizione LocalConfiguration locale. Ogni volta che cambia, le funzioni componibili che leggono da LocalConfiguration.current si ricompongono. Per informazioni su come funzionano i locali delle composizioni, consulta Dati con ambito locale con ComposeLocal.

Esempio

Nel seguente esempio, un componibile mostra una data con un formato specifico. Il componibile reagisce alle modifiche alla configurazione delle impostazioni internazionali del sistema chiamando ConfigurationCompat.getLocales() con LocalConfiguration.current.

@Composable
fun DateText(year: Int, dayOfYear: Int) {
    val dateTimeFormatter = DateTimeFormatter.ofPattern(
        "MMM dd",
        ConfigurationCompat.getLocales(LocalConfiguration.current)[0]
    )
    Text(
        dateTimeFormatter.format(LocalDate.ofYearDay(year, dayOfYear))
    )
}

Per evitare la ricreazione di Activity quando le impostazioni internazionali vengono modificate, il Activity che ospita il codice Scrivi deve disattivare le modifiche alla configurazione delle impostazioni internazionali. Per farlo, imposta android:configChanges su locale|layoutDirection.

Modifiche alla configurazione: concetti chiave e best practice

Di seguito sono riportati i concetti chiave che è necessario conoscere quando si lavora alle modifiche alla configurazione:

  • Configurazioni: le configurazioni dei dispositivi definiscono il modo in cui l'interfaccia utente viene mostrata all'utente, ad esempio dimensioni di visualizzazione dell'app, impostazioni internazionali o tema di sistema.
  • Modifiche alla configurazione:le configurazioni cambiano tramite l'interazione dell'utente. Ad esempio, l'utente potrebbe modificare le impostazioni del dispositivo o il modo in cui interagisce fisicamente con il dispositivo. non c'è modo di impedire le modifiche alla configurazione.
  • Activity ricreazione: le modifiche alla configurazione comportano Activity ricreazione per impostazione predefinita. Si tratta di un meccanismo integrato per reinizializzare lo stato dell'app per la nuova configurazione.
  • Eliminazione di Activity: la ricreazione di Activity causa l'eliminazione della vecchia istanza Activity da parte del sistema e la creazione di una nuova in sostituzione. L'istanza precedente ora è obsoleta. Eventuali riferimenti rimanenti comportano perdite di memoria, bug o arresti anomali.
  • Stato: lo stato nella precedente istanza Activity non è presente nella nuova istanza Activity perché si tratta di due istanze di oggetto diverse. Conservare l'app e lo stato dell'utente come descritto in Salvare gli stati dell'interfaccia utente.
  • Disattivazione: la disattivazione della ricreazione dell'attività per un tipo di modifica alla configurazione è una potenziale ottimizzazione. Richiede che l'app venga aggiornata correttamente in reazione alla nuova configurazione.

Per offrire una buona esperienza utente, segui le seguenti best practice:

  • Preparati a modifiche frequenti alla configurazione: non dare per scontato che le modifiche alla configurazione siano rare o non avvengano mai, indipendentemente dal livello API, dal fattore di forma o dal toolkit dell'interfaccia utente. Quando un utente causa una modifica alla configurazione, si aspetta che le app vengano aggiornate e continuino a funzionare correttamente con la nuova configurazione.
  • Conserva stato: non perdere lo stato dell'utente quando è in corso la ricreazione di Activity. Mantieni lo stato come descritto in Salvare gli stati dell'interfaccia utente.
  • Evita la disattivazione rapida: non disattivare la ricreazione di Activity come scorciatoia per evitare la perdita di stato. Se disattivi la ricreazione dell'attività, devi mantenere la promessa di gestire la modifica e puoi comunque perdere lo stato a causa della nuova creazione di Activity da altre modifiche alla configurazione, dell'interruzione dell'elaborazione o della chiusura dell'app. È impossibile disattivare completamente la riattivazione di Activity. Mantieni lo stato come descritto in Salvare gli stati dell'interfaccia utente.
  • Non evitare modifiche alla configurazione: non applicare limitazioni all'orientamento, alle proporzioni o alla ridimensionabilità per evitare modifiche alla configurazione e la nuova creazione di Activity. Questo ha un impatto negativo sugli utenti che vogliono utilizzare la tua app nel modo che preferiscono.

Gestire le modifiche alla configurazione in base alle dimensioni

Le modifiche alla configurazione in base alle dimensioni possono essere apportate in qualsiasi momento e hanno maggiori probabilità quando la tua app viene eseguita su un dispositivo con schermo grande su cui gli utenti possono attivare la modalità multi-finestra. Si aspettano che la tua app funzioni bene in quell'ambiente.

Esistono due tipi generali di modifiche delle dimensioni: significative e insignificanti. Una modifica significativa delle dimensioni è quella in cui un insieme diverso di risorse alternative viene applicato alla nuova configurazione a causa di una differenza nelle dimensioni dello schermo, ad esempio larghezza, altezza o larghezza minima. Queste risorse includono quelle definite dall'app e quelle di qualsiasi sua libreria.

Limita la ricreazione delle attività per le modifiche alla configurazione in base alle dimensioni

Quando disattivi la ricreazione di Activity per le modifiche alla configurazione in base alle dimensioni, il sistema non ricrea Activity. Riceve invece una chiamata a Activity.onConfigurationChanged(). Tutte le viste collegate ricevono una chiamata a View.onConfigurationChanged().

La ricreazione di Activity è disabilitata per le modifiche alla configurazione in base alle dimensioni quando è presente android:configChanges="screenSize|smallestScreenSize|orientation|screenLayout" nel file manifest.

Consenti la ricreazione dell'attività per le modifiche alla configurazione in base alle dimensioni

Su Android 7.0 (livello API 24) e versioni successive, la ricreazione Activity avviene solo per le modifiche alla configurazione in base alle dimensioni se la variazione delle dimensioni è significativa. Quando il sistema non ricrea un Activity a causa di dimensioni insufficienti, potrebbe invece chiamare Activity.onConfigurationChanged() e View.onConfigurationChanged().

Devi osservare alcune avvertenze in merito ai callback Activity e View quando Activity non viene ricreato:

  • Da Android 11 (livello API 30) ad Android 13 (livello API 33), Activity.onConfigurationChanged() non viene chiamato.
  • Esiste un problema noto per cui View.onConfigurationChanged() potrebbe non essere chiamato in alcuni casi su Android 12L (livello API 32) e nelle versioni precedenti di Android 13 (livello API 33). Per ulteriori informazioni, consulta questo problema pubblico. Questo problema è stato risolto nelle successive release di Android 13 e Android 14.

Per il codice che dipende dall'ascolto delle modifiche alla configurazione in base alle dimensioni, consigliamo di utilizzare un'utilità View con un elemento View.onConfigurationChanged() con override, anziché fare affidamento su Activityricreazione o Activity.onConfigurationChanged().