Umgang mit Konfigurationsänderungen

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

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

Die meisten Konfigurationsänderungen sind auf Nutzerinteraktionen zurückzuführen. Wenn du das Gerät beispielsweise drehst oder faltst, ändert sich der für deine App verfügbare Bildschirmplatz. Ebenso werden durch das Ändern von Geräteeinstellungen wie Schriftgröße, Sprache oder bevorzugtes Design die entsprechenden Werte im Configuration-Objekt geändert.

Diese Parameter erfordern in der Regel ausreichend umfangreiche Änderungen an der Benutzeroberfläche Ihrer App, dass die Android-Plattform über einen speziellen Mechanismus für Änderungen verfügt. Bei diesem Mechanismus handelt es sich um eine Activity-Neuerstellung.

Aktivitäten und Freizeitaktivitäten

Bei einer Konfigurationsänderung erstellt das System ein Activity neu. Dazu ruft das System onDestroy() auf und löscht die vorhandene Activity-Instanz. Anschließend wird eine neue Instanz mit onCreate() erstellt und diese neue Activity-Instanz wird mit der neuen, aktualisierten Konfiguration initialisiert. Das bedeutet auch, dass das System auch die UI mit der neuen Konfiguration neu erstellt.

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

Beispiel für Freizeitaktivitäten

Ein Beispiel: TextView zeigt einen statischen Titel mithilfe von android:text="@string/title" an, wie in einer Layout-XML-Datei definiert. Wenn die Ansicht erstellt wird, wird der Text basierend auf der aktuellen Sprache genau einmal 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.

Bei der Neuerstellung werden auch alle Status gelöscht, die als Felder im Activity oder in einem der darin enthaltenen Fragment-, View- oder anderen Objekte beibehalten werden. Das liegt daran, dass bei der Neuerstellung von Activity eine völlig neue Instanz von Activity und der UI erstellt wird. Außerdem ist die alte Activity nicht mehr sichtbar oder gültig, sodass alle verbleibenden Verweise auf sie oder ihre darin enthaltenen Objekte veraltet sind. Sie können Programmfehler, Speicherlecks und Abstürze verursachen.

Erwartungen der Nutzenden

Der Nutzer einer App erwartet, dass der Status beibehalten wird. Wenn ein Nutzer ein Formular ausfüllt und eine andere App im Mehrfenstermodus öffnet, um auf Informationen zu verweisen, beeinträchtigt dies die Nutzerfreundlichkeit, wenn er zu einem gelöschten Formular oder zu einer anderen Stelle in der App zurückkehrt. Als Entwickler müssen Sie durch Konfigurationsänderungen und die Neuerstellung von Aktivitäten für eine einheitliche Nutzererfahrung sorgen.

Wenn Sie prüfen möchten, ob der Status in Ihrer Anwendung erhalten bleibt, können Sie Aktionen ausführen, die zu Konfigurationsänderungen führen, während die Anwendung im Vordergrund und im Hintergrund ausgeführt wird. Diese Aktivitäten umfassen:

  • Gerät drehen
  • Mehrfenstermodus wird gestartet
  • Größenanpassung der Anwendung im Mehrfenstermodus oder in einem Freiformfenster
  • Zusammenklappen eines faltbaren Geräts mit mehreren Displays
  • Ändern des Systemdesigns, z. B. dunkler oder heller Modus
  • Ändern der Schriftgröße
  • System- oder App-Sprache ändern
  • Hardwaretastatur verbinden oder trennen
  • Dock verbinden oder trennen

Es gibt drei primäre Ansätze, mit denen Sie den relevanten Status durch die Neuerstellung von Activity erhalten können. Welche Version Sie verwenden sollten, hängt von der Art des Status ab, den Sie beibehalten möchten:

  • Lokale Persistenz zur Bewältigung von Prozesstod bei komplexen oder großen Daten. Zum nichtflüchtigen lokalen Speicher gehören Datenbanken oder DataStore.
  • Beibehaltene Objekte wie ViewModel-Instanzen zur Verarbeitung von UI-bezogenen Status im Arbeitsspeicher, während der Nutzer die App aktiv verwendet.
  • Gespeicherter Instanzstatus, um den vom System initiierten Prozessabbruch zu verarbeiten und den vorübergehenden Status beizubehalten, der von der Nutzereingabe oder -navigation abhängt

Weitere Informationen zu den APIs für die einzelnen APIs und deren Verwendung finden Sie unter UI-Status speichern.

Wiederherstellung von Aktivitäten einschränken

Sie können die automatische Wiederherstellung von Aktivitäten bei bestimmten Konfigurationsänderungen verhindern. Die Neuerstellung von Activity führt dazu, dass die gesamte UI und alle aus Activity abgeleiteten Objekte neu erstellt werden. Es gibt gute Gründe, dies zu vermeiden. Beispielsweise kann es sein, dass Ihre Anwendung während einer bestimmten Konfigurationsänderung keine Ressourcen aktualisieren muss oder Sie eine Leistungsbeschränkung haben. In diesem Fall können Sie erklären, dass Ihre Aktivität die Konfigurationsänderung selbst übernimmt, und verhindern, dass das System Ihre Aktivität neu startet.

Wenn Sie die Neuerstellung von Aktivitäten bei bestimmten 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 für das Attribut android:configChanges.

Mit dem folgenden Manifestcode wird die Neuerstellung von Activity für MyActivity deaktiviert, wenn sich die Bildschirmausrichtung und die Tastaturverfügbarkeit ä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. Sie können beispielsweise die in Android 12L (API-Level 32) eingeführte dynamische Farbänderung nicht deaktivieren.

Auf Konfigurationsänderungen im View-System reagieren

Bei einer Konfigurationsänderung, für die Sie im View-System die Neuerstellung von Activity deaktiviert haben, erhält die Aktivität einen Aufruf an Activity.onConfigurationChanged(). Alle angehängten Ansichten erhalten auch einen Aufruf von 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 Ihre 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 Ihrer Aktivität so aktualisiert, dass es Ressourcen basierend auf der neuen Konfiguration zurückgibt. So können Sie Elemente Ihrer UI zurücksetzen, ohne dass das System Ihre Aktivität neu startet.

Mit 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 stattdessen onConfigurationChanged() nicht implementieren. In diesem Fall werden alle vor der Konfigurationsänderung verwendeten Ressourcen weiterhin verwendet und Sie haben nur den Neustart Ihrer Aktivität vermieden. So kann es z. B. sein, dass eine TV-App nicht reagieren möchte, wenn eine Bluetooth-Tastatur verbunden oder getrennt wird.

Status beibehalten

Bei dieser Methode muss der Zustand während des normalen Aktivitätslebenszyklus beibehalten werden. Das hat folgende Gründe:

  • Unvermeidliche Änderungen:Wenn Sie Konfigurationsänderungen nicht verhindern, können Sie Ihre Anwendung neu starten.
  • Process Tod:Ihre Anwendung muss in der Lage sein, den vom System initiierten Prozesstod zu verarbeiten. Wenn der Nutzer die Anwendung verlässt und in den Hintergrund geht, wird die App möglicherweise vom System zerstört.

Auf Konfigurationsänderungen in Jetpack Compose reagieren

Mit Jetpack Compose kann Ihre Anwendung leichter auf Konfigurationsänderungen reagieren. Wenn Sie jedoch die Neuerstellung von Activity für alle Konfigurationsänderungen deaktivieren, muss Ihre Anwendung Konfigurationsänderungen trotzdem korrekt verarbeiten.

Das Objekt Configuration ist in der Hierarchie der Erstellungs-UI mit der lokalen Zusammensetzung LocalConfiguration verfügbar. Bei jeder Änderung werden zusammensetzbare Funktionen, die aus LocalConfiguration.current lesen, wieder zusammengesetzt. Informationen zur Funktionsweise von Kompositionslokalen finden Sie unter Lokal skalierte Daten mit CompositionLocal.

Beispiel

Im folgenden Beispiel zeigt eine zusammensetzbare Funktion ein Datum in einem bestimmten Format an. Die zusammensetzbare Funktion reagiert auf Änderungen der Sprachkonfiguration des Systems, indem sie ConfigurationCompat.getLocales() mit LocalConfiguration.current aufruft.

@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 zu vermeiden, dass Activity bei einer Änderung der Sprache neu erstellt wird, muss der Activity, der den Composer-Code hostet, Änderungen der Sprachkonfiguration deaktivieren. Dazu setzen Sie android:configChanges auf locale|layoutDirection.

Konfigurationsänderungen: Schlüsselkonzepte 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 dem Nutzer angezeigt wird, z. B. Anzeigegröße der App, Sprache oder Systemdesign.
  • Konfigurationsänderungen:Konfigurationen ändern sich durch Nutzerinteraktionen. Der Nutzer kann beispielsweise die Geräteeinstellungen oder die physische Interaktion mit dem Gerät ändern. Es gibt keine Möglichkeit, Konfigurationsänderungen zu verhindern.
  • Neuerstellung von Activity: 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.
  • Löschen Activity: Bei der Neuerstellung von Activity löscht das System die alte Activity-Instanz und erstellt stattdessen eine neue. Die alte Instanz ist jetzt veraltet. Alle verbleibenden Verweise darauf führen zu Speicherlecks, Programmfehlern oder Abstürzen.
  • Status: Der Status in der alten Activity-Instanz ist in der neuen Activity-Instanz nicht vorhanden, da es sich um zwei verschiedene Objektinstanzen handelt. Sie können die App und den Nutzerstatus wie unter UI-Status speichern beschrieben beibehalten.
  • Deaktivieren:Eine mögliche Optimierung ist das Deaktivieren der Neuerstellung von Aktivitäten für eine Art von Konfigurationsänderung. Ihre Anwendung muss als Reaktion auf die neue Konfiguration ordnungsgemäß aktualisiert werden.

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

  • Auf häufige Konfigurationsänderungen vorbereitet sein:Sie sollten nicht davon ausgehen, dass Konfigurationsänderungen selten oder nie auftreten, unabhängig von API-Ebene, Formfaktor oder UI-Toolkit. Wenn ein Nutzer eine Konfigurationsänderung verursacht, erwartet er, dass Apps aktualisiert werden und weiterhin korrekt mit der neuen Konfiguration funktionieren.
  • Status beibehalten: Der Status des Nutzers wird nicht verloren, wenn eine Activity-Neuerstellung durchgeführt wird. Behalten Sie den Status wie unter UI-Zustände speichern beschrieben bei.
  • Deaktivieren Sie die Funktion nicht als schnelle Lösung:Deaktivieren Sie nicht die Neuerstellung von Activity als Verknüpfung, um einen Verlust des Status zu vermeiden. Wenn du die Wiederherstellung von Aktivitäten deaktivierst, musst du das Versprechen zur Verarbeitung der Änderung einhalten. Du kannst den Status trotzdem verlieren, wenn Activity aus anderen Konfigurationsänderungen wiederhergestellt, der Tod verarbeitet oder die App geschlossen wird. Es ist nicht möglich, die Neuerstellung von Activity vollständig zu deaktivieren. Behalten Sie den Status wie unter UI-Zustände speichern beschrieben bei.
  • Vermeiden Sie keine Konfigurationsänderungen: Schränken Sie die Ausrichtung, das Seitenverhältnis oder die Größe nicht ein, um Konfigurationsänderungen und die Neuerstellung von Activity zu vermeiden. Das wirkt sich negativ auf Nutzer aus, die Ihre App in ihrer bevorzugten Art verwenden möchten.

Größenbasierte Konfigurationsänderungen verarbeiten

Größenbasierte Konfigurationsänderungen können jederzeit vorgenommen werden. Die Wahrscheinlichkeit ist größer, wenn Ihre App auf einem Gerät mit großen Bildschirmen ausgeführt wird, auf dem Nutzer in den Mehrfenstermodus wechseln können. Er erwartet, dass Ihre Anwendung in dieser Umgebung gut funktioniert.

Es gibt zwei allgemeine Arten von Größenänderungen: signifikant und unbedeutend. Bei einer erheblichen Größenänderung gilt eine andere Gruppe alternativer Ressourcen für die neue Konfiguration, da sich die Bildschirmgröße unterscheidet, z. B. Breite, Höhe oder kleinste Breite. Zu diesen Ressourcen gehören Ressourcen, die von der App selbst definiert wurden, sowie Ressourcen, die aus ihren Bibliotheken stammen.

Neuerstellung von Aktivitäten für größenbasierte Konfigurationsänderungen einschränken

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

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

Neuerstellung von Aktivitäten für größenbasierte Konfigurationsänderungen zulassen

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

Es gibt einige Einschränkungen in Bezug auf die Activity- und View-Callbacks, wenn 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() in einigen Fällen unter Android 12L (API-Level 32) und frühen Versionen von Android 13 (API-Level 33) möglicherweise nicht aufgerufen wird. Weitere Informationen finden Sie in dieser öffentlichen Ausgabe. In späteren Releases von Android 13 und Android 14 wurde dieses Problem inzwischen behoben.

Für Code, der auf größenbasierte Konfigurationsänderungen warten muss, empfehlen wir die Verwendung eines Dienstprogramms View mit einem überschriebenen View.onConfigurationChanged(), anstatt eine Activity-Neuerstellung oder Activity.onConfigurationChanged() zu verwenden.