Umgang mit Konfigurationsänderungen

Einige Gerätekonfigurationen können sich ändern, während die App ausgeführt wird. Dazu gehören unter anderem:

  • Displaygröße der App
  • Bildschirmausrichtung
  • Schriftgröße und -stärke
  • Sprache
  • Dunkler Modus und heller Modus
  • Verfügbarkeit der Tastatur

Die meisten dieser Konfigurationsänderungen erfolgen aufgrund einer Nutzerinteraktion. Wenn Sie das Gerät beispielsweise drehen oder zusammenklappen, ändert sich die für Ihre App verfügbare Bildschirmfläche. Wenn Sie Geräteeinstellungen wie die Schriftgröße, die Sprache oder das bevorzugte Design ändern, ändern sich auch die entsprechenden Werte im Configuration-Objekt.

Diese Parameter erfordern in der Regel so große Änderungen an der Benutzeroberfläche Ihrer Anwendung, dass die Android-Plattform einen speziellen Mechanismus für den Fall der Änderung hat. Dieser Mechanismus wird als Activity-Wiederherstellung bezeichnet.

Aktivitätswiedergabe

Das System erstellt eine Activity neu, wenn eine Konfigurationsänderung erfolgt. Dazu ruft das System onDestroy() auf und zerstört die vorhandene Activity-Instanz. Anschließend wird mit onCreate() eine neue Instanz erstellt. Diese neue Activity-Instanz wird mit der neuen, aktualisierten Konfiguration initialisiert. Das bedeutet auch, dass das System die Benutzeroberfläche mit der neuen Konfiguration neu erstellt.

Das Verhalten beim Neuaufbau hilft Ihrer Anwendung, sich an neue Konfigurationen anzupassen, indem sie automatisch mit alternativen Ressourcen neu geladen wird, die der neuen Gerätekonfiguration entsprechen.

Beispiel für die Freizeit

Angenommen, eine TextView zeigt einen statischen Titel mit android:text="@string/title" an, wie in einer Layout-XML-Datei definiert. Beim Erstellen der Ansicht wird der Text genau einmal basierend auf der aktuellen Sprache festgelegt. Wenn sich die Sprache ändert, erstellt das System die Aktivität neu. Daher erstellt das System auch die Ansicht neu und initialisiert sie mit dem richtigen Wert basierend auf der neuen Sprache.

Außerdem werden alle Status gelöscht, die als Felder in Activity oder in einem der enthaltenen Fragment-, View- oder anderen Objekte gespeichert sind. Das liegt daran, dass beim Erstellen einer neuen Activity-Instanz eine völlig neue Instanz der Activity und der Benutzeroberfläche erstellt wird. Außerdem ist die alte Activity nicht mehr sichtbar oder gültig. Alle verbleibenden Verweise darauf oder auf die darin enthaltenen Objekte sind veraltet. Sie können zu Fehlern, Speicherlecks und Abstürzen führen.

Erwartungen der Nutzer

Nutzer einer App erwarten, dass der Status beibehalten wird. Wenn ein Nutzer ein Formular ausfüllt und eine andere App im Multifenstermodus öffnet, um Informationen zu sehen, ist es nicht optimal, wenn er zu einem leeren Formular oder an eine andere Stelle in der App zurückkehrt. Als Entwickler müssen Sie durch Konfigurationsänderungen und das Nachstellen von Aktivitäten für eine einheitliche Nutzererfahrung sorgen.

Um zu prüfen, ob der Status in Ihrer Anwendung beibehalten wird, können Sie Aktionen ausführen, die sowohl bei aktiviertem als auch bei deaktiviertem Status der App zu Konfigurationsänderungen führen. Diese Aktivitäten umfassen:

  • Gerät drehen
  • Mehrfenstermodus starten
  • Größe der Anwendung im Mehrfenstermodus oder in einem freiformigen Fenster ändern
  • Ein faltbares Gerät mit mehreren Displays zusammenfalten
  • Systemdesign ändern, z. B. dunkles Design oder helles Design
  • Schriftgröße ändern
  • System- oder App-Sprache ändern
  • Hardwaretastatur verbinden oder trennen
  • Dock anschließen oder trennen

Es gibt drei Hauptansätze, mit denen Sie den relevanten Status durch Activity-Wiederherstellung beibehalten können. Welche Sie verwenden, hängt davon ab, welchen Status Sie beibehalten möchten:

  • Lokale Persistenz, um den Prozesstod bei komplexen oder großen Daten zu behandeln. Zum dauerhaften lokalen Speicher gehören Datenbanken oder DataStore.
  • Beibehaltene Objekte wie ViewModel-Instanzen, um den UI-bezogenen Status im Arbeitsspeicher zu verwalten, während der Nutzer die App aktiv nutzt.
  • Gespeicherter Instanzstatus, um systeminitiierte Prozessbeendigungen zu verarbeiten und einen sitzungsspezifischen Status beizubehalten, der von der Nutzereingabe oder Navigation abhängt.

Weitere Informationen zu den APIs für diese Funktionen und dazu, wann die jeweilige Verwendung sinnvoll ist, finden Sie unter UI-Status speichern.

Wiedergabe von Aktivitäten einschränken

Sie können die automatische Wiederherstellung von Aktivitäten bei bestimmten Konfigurationsänderungen verhindern. Wenn Sie Activity neu erstellen, wird die gesamte Benutzeroberfläche und alle vom Activity abgeleiteten Objekte neu erstellt. Möglicherweise haben Sie gute Gründe, dies zu vermeiden. Beispielsweise müssen bei einer bestimmten Konfigurationsänderung möglicherweise keine Ressourcen in Ihrer App aktualisiert werden oder es liegt eine Leistungseinschränkung vor. In diesem Fall können Sie angeben, dass Ihre Aktivität die Konfigurationsänderung selbst verarbeitet, und verhindern, dass das System Ihre Aktivität neu startet.

Wenn Sie die Wiederherstellung von Aktivitäten für bestimmte Konfigurationsänderungen deaktivieren möchten, fügen Sie den Konfigurationstyp in der Datei AndroidManifest.xml im Eintrag <activity> zu android:configChanges hinzu. Mögliche Werte finden Sie in der Dokumentation zum Attribut android:configChanges.

Mit dem folgenden Manifest-Code wird die Neuerstellung von Activity für MyActivity deaktiviert, wenn sich die Bildschirmausrichtung und die Verfügbarkeit der Tastatur ändern:

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

Einige Konfigurationsänderungen führen immer dazu, dass die Aktivität neu gestartet wird. Sie können sie nicht deaktivieren. So können Sie beispielsweise die dynamische Farbänderung, die in Android 12L (API-Level 32) eingeführt wurde, nicht deaktivieren.

Auf Konfigurationsänderungen im View-System reagieren

Wenn im View-System eine Konfigurationsänderung auftritt, für die Sie die Neuerstellung von Activity deaktiviert haben, wird die Aktivität auf Activity.onConfigurationChanged() verwiesen. Alle angehängten Ansichten erhalten auch einen Aufruf an View.onConfigurationChanged(). Bei Konfigurationsänderungen, die Sie android:configChanges nicht hinzugefügt haben, erstellt das System die Aktivität wie gewohnt neu.

Die Callback-Methode onConfigurationChanged() empfängt ein Configuration-Objekt, das die neue Gerätekonfiguration angibt. Lesen Sie die Felder im Configuration-Objekt, um die neue Konfiguration zu ermitteln. Aktualisieren Sie die Ressourcen, die Sie in Ihrer Benutzeroberfläche verwenden, um die nachfolgenden Änderungen vorzunehmen. Wenn das System diese Methode aufruft, wird das Resources-Objekt deiner Aktivität aktualisiert, um Ressourcen basierend auf der neuen Konfiguration zurückzugeben. So können Sie Elemente der Benutzeroberfläche zurücksetzen, ohne dass das System Ihre Aktivitäten neu startet.

In der folgenden onConfigurationChanged()-Implementierung wird beispielsweise geprüft, ob eine Tastatur verfügbar ist:

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();
    }
}

Wenn Sie Ihre Anwendung aufgrund dieser Konfigurationsänderungen nicht aktualisieren müssen, können Sie onConfigurationChanged() auch nicht implementieren. In diesem Fall werden alle vor der Konfigurationsänderung verwendeten Ressourcen weiterhin verwendet und Sie haben nur den Neustart Ihrer Aktivitäten verhindert. Beispielsweise soll eine Fernseher-App nicht reagieren, wenn eine Bluetooth-Tastatur angeschlossen oder getrennt wird.

Status beibehalten

Bei dieser Methode müssen Sie den Status während des normalen Aktivitätszyklus beibehalten. Das hat folgende Gründe:

  • Unvermeidliche Änderungen:Konfigurationsänderungen, die Sie nicht verhindern können, können dazu führen, dass Ihre Anwendung neu gestartet wird.
  • Prozessbeendigung: Ihre Anwendung muss mit einer systeminitiierten Prozessbeendigung umgehen können. Wenn der Nutzer Ihre Anwendung verlässt und die App in den Hintergrund wechselt, wird sie möglicherweise vom System beendet.

Auf Konfigurationsänderungen in Jetpack Compose reagieren

Mit Jetpack Compose kann Ihre App einfacher auf Konfigurationsänderungen reagieren. Wenn Sie die Neuerstellung von Activity für alle Konfigurationsänderungen deaktivieren, die dies zulassen, muss Ihre App Konfigurationsänderungen jedoch weiterhin korrekt verarbeiten.

Das Objekt Configuration ist in der Compose-UI-Hierarchie verfügbar, wobei die LocalConfiguration-Komposition lokal ist. Jedes Mal, wenn sich der Wert ändert, werden zusammensetzbare Funktionen, die aus LocalConfiguration.current gelesen werden, neu zusammengesetzt. Informationen zur Funktionsweise von lokalen Kompositionen finden Sie unter Daten mit lokalem Umfang mit CompositionLocal.

Verwendungsbeispiele

Im folgenden Beispiel wird in einem Composeable ein Datum in einem bestimmten Format angezeigt. Das Composeable reagiert auf Konfigurationsänderungen der Systemsprache, indem ConfigurationCompat.getLocales() mit LocalConfiguration.current aufgerufen wird.

@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))
    )
}

Um eine Neuerstellung von Activity bei Änderungen der Sprache zu vermeiden, muss der Activity, auf dem der Compose-Code gehostet wird, Änderungen an der Sprachenkonfiguration deaktivieren. Legen Sie dazu android:configChanges auf locale|layoutDirection fest.

Konfigurationsänderungen: Wichtige Konzepte und Best Practices

Dies sind die wichtigsten Konzepte, die Sie bei der Arbeit an Konfigurationsänderungen kennen sollten:

  • Konfigurationen: Gerätekonfigurationen legen fest, wie die Benutzeroberfläche für den Nutzer angezeigt wird, z. B. die App-Bildschirmgröße, die Sprache oder das Systemdesign.
  • Konfigurationsänderungen:Konfigurationen ändern sich durch Nutzerinteraktionen. So kann der Nutzer beispielsweise die Geräteeinstellungen ändern oder die Art und Weise, wie er mit dem Gerät interagiert. Es gibt keine Möglichkeit, Konfigurationsänderungen zu verhindern.
  • Activity recreation:Konfigurationsänderungen führen standardmäßig zur Neuerstellung von Activity. Dies ist ein integrierter Mechanismus, mit dem der App-Status für die neue Konfiguration neu initialisiert wird.
  • Activity-Löschen: Beim erneuten Erstellen von Activity löscht das System die alte Activity-Instanz und erstellt an ihrer Stelle eine neue. Die alte Instanz ist jetzt nicht mehr verfügbar. Alle verbleibenden Verweise darauf führen zu Speicherlecks, Fehlern oder Abstürzen.
  • Status:Der Status in der alten Activity-Instanz ist nicht in der neuen Activity-Instanz vorhanden, da es sich um zwei verschiedene Objektinstanzen handelt. Speichern Sie den Status der App und des Nutzers wie unter UI-Status speichern beschrieben.
  • Deaktivierung:Die Aktivitätswiederherstellung für eine Art von Konfigurationsänderung zu deaktivieren, ist eine mögliche Optimierung. Dazu muss Ihre App auf die neue Konfiguration reagieren und entsprechend aktualisiert werden.

Beachten Sie die folgenden Best Practices, um eine gute Nutzererfahrung zu bieten:

  • Auf häufige Konfigurationsänderungen vorbereitet sein: Gehen Sie nicht davon aus, dass Konfigurationsänderungen selten oder nie vorkommen, unabhängig von API-Ebene, Formfaktor oder UI-Toolkit. Wenn ein Nutzer eine Konfigurationsänderung vornimmt, erwartet er, dass Apps aktualisiert werden und auch mit der neuen Konfiguration ordnungsgemäß funktionieren.
  • Status beibehalten:Der Status des Nutzers darf nicht verloren gehen, wenn Activity neu erstellt wird. Bewahren Sie den Status wie unter UI-Status speichern beschrieben auf.
  • Deaktivierung als schnelle Lösung vermeiden:Deaktivieren Sie die Activity-Wiederherstellung nicht als Abkürzung, um den Verlust von Status zu vermeiden. Wenn Sie die Wiederherstellung von Aktivitäten deaktivieren, müssen Sie die Änderung selbst vornehmen. Der Status kann jedoch durch die Wiederherstellung von Activity aufgrund anderer Konfigurationsänderungen, durch den Prozesstod oder durch das Schließen der App verloren gehen. Die Wiederherstellung von Activity kann nicht vollständig deaktiviert werden. Bewahren Sie den Status wie unter UI-Status speichern beschrieben auf.
  • Vermeiden Sie keine Konfigurationsänderungen:Legen Sie keine Einschränkungen für Ausrichtung, Seitenverhältnis oder Größe fest, um Konfigurationsänderungen und die Neuerstellung von Activity zu vermeiden. Das wirkt sich negativ auf Nutzer aus, die Ihre App auf ihre bevorzugte Weise verwenden möchten.

Größebasierte Konfigurationsänderungen verarbeiten

Größebasierte Konfigurationsänderungen können jederzeit auftreten und sind wahrscheinlicher, wenn Ihre App auf einem Gerät mit großem Bildschirm ausgeführt wird, auf dem Nutzer den Multifenstermodus aktivieren können. Sie erwarten, dass Ihre App in dieser Umgebung gut funktioniert.

Es gibt zwei Arten von Größenänderungen: signifikant und nicht signifikant. Bei einer erheblichen Größenänderung gilt für die neue Konfiguration aufgrund einer anderen Bildschirmgröße (z. B. Breite, Höhe oder kleinste Breite) eine andere Gruppe von alternativen Ressourcen. Dazu gehören die von der App selbst definierten Ressourcen und die aus einer ihrer Bibliotheken.

Wiederherstellung von Aktivitäten bei größenbasierten Konfigurationsänderungen einschränken

Wenn Sie die Neuerstellung von Activity für größenbasierte Konfigurationsänderungen deaktivieren, wird die Activity vom System nicht neu erstellt. Stattdessen wird ein Aufruf an Activity.onConfigurationChanged() gesendet. Alle angehängten Ansichten erhalten einen Aufruf an View.onConfigurationChanged().

Die Neuerstellung von Activity ist für größenbasierte Konfigurationsänderungen deaktiviert, wenn Sie android:configChanges="screenSize|smallestScreenSize|orientation|screenLayout" in Ihrer Manifestdatei haben.

Aktivitätswiederherstellung bei größenbasierten Konfigurationsänderungen zulassen

Unter Android 7.0 (API-Level 24) und höher wird Activity nur bei größenbasierten Konfigurationsänderungen neu erstellt, wenn die Größenänderung erheblich ist. Wenn das System eine Activity aufgrund unzureichender Größe nicht neu erstellt, ruft es stattdessen möglicherweise Activity.onConfigurationChanged() und View.onConfigurationChanged() auf.

Bei den Activity- und View-Callbacks gibt es einige Einschränkungen, wenn die Activity nicht neu erstellt wird:

  • Unter Android 11 (API-Level 30) bis Android 13 (API-Level 33) wird Activity.onConfigurationChanged() nicht aufgerufen.
  • Es gibt ein bekanntes Problem, bei dem View.onConfigurationChanged() unter Android 12L (API-Level 32) und frühen Versionen von Android 13 (API-Level 33) in einigen Fällen nicht aufgerufen wird. Weitere Informationen finden Sie in diesem öffentlichen Problem. Dieses Problem wurde in späteren Android 13-Releases und in Android 14 behoben.

Für Code, der auf das Überwachen von größenbasierten Konfigurationsänderungen angewiesen ist, empfehlen wir die Verwendung eines Dienstprogramms View mit einer überschriebenen View.onConfigurationChanged() anstelle der Neuerstellung von Activity oder Activity.onConfigurationChanged().