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 und heller Modus im Vergleich
  • Tastaturverfügbarkeit

Die meisten dieser Konfigurationsänderungen sind auf Nutzerinteraktionen zurückzuführen. Wenn Sie das Gerät beispielsweise drehen oder zusammenklappen, ändert sich der für Ihre App verfügbare Platz auf dem Bildschirm. Wenn Sie Geräteeinstellungen wie Schriftgröße, Sprache oder bevorzugtes Design ändern, werden auch die entsprechenden Werte im Objekt Configuration geändert.

Diese Parameter erfordern in der Regel ausreichend große Änderungen an der Benutzeroberfläche Ihrer Anwendung, sodass die Android-Plattform über einen speziellen Mechanismus verfügt, wenn sie sich ändern. Dieser Mechanismus ist die Neuerstellung von Activity.

Freizeitaktivitäten

Das System erstellt ein Activity neu, wenn eine Konfigurationsänderung erfolgt. Dazu ruft das System onDestroy() auf und löscht die vorhandene Activity-Instanz. Anschließend wird mit onCreate() eine neue Instanz erstellt und diese neue Activity-Instanz mit der neuen, aktualisierten Konfiguration initialisiert. Dies 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, indem sie automatisch mit alternativen Ressourcen neu geladen wird, die der neuen Gerätekonfiguration entsprechen.

Beispiel für Freizeit

Nehmen wir als Beispiel ein TextView, das einen statischen Titel mithilfe von android:text="@string/title" anzeigt, wie in einer Layout-XML-Datei definiert. Wenn die Ansicht erstellt wird, wird der Text genau einmal basierend auf der aktuellen Sprache festgelegt. Wenn sich die Sprache ändert, erstellt das System die Aktivität neu. Folglich 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 enthaltenen Fragment-, View- oder anderen Objekte gespeichert sind. Das liegt daran, dass bei der Neuerstellung von Activity eine vollständig 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 die enthaltenen Objekte veraltet sind. Sie können Programmfehler, Speicherlecks und Abstürze verursachen.

Erwartungen der Nutzenden

Der Nutzer einer Anwendung 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, ist es für den Nutzer schlecht, 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 konsistente Nutzererfahrung sorgen.

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

  • Drehen des Geräts
  • Mehrfenstermodus wird aktiviert
  • Größe der Anwendung im Mehrfenstermodus oder in einem Freiformfenster anpassen
  • Faltbares Gerät mit mehreren Displays
  • Systemdesign ändern, z. B. dunkler oder heller Modus
  • Schriftgröße ändern
  • 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 beibehalten können. Welche Sie verwenden sollten, hängt von der Art des Status ab, den Sie beibehalten möchten:

  • Lokale Persistenz für den Umgang mit Prozessbeendigung bei komplexen oder großen Daten. Der nichtflüchtige lokale Speicher umfasst Datenbanken oder DataStore.
  • Beibehaltene Objekte wie ViewModel-Instanzen, um den UI-bezogenen Status im Arbeitsspeicher zu verarbeiten, während der Nutzer die App aktiv verwendet.
  • Gespeicherter Instanzstatus, um den beendeten Prozess zu verarbeiten und den vorübergehenden Status beizubehalten, der von der Nutzereingabe oder -navigation abhängt.

Ausführliche Informationen zu den jeweiligen APIs und deren Verwendung finden Sie unter UI-Status speichern.

Freizeitaktivitäten einschränken

Sie können die automatische Wiederherstellung von Aktivitäten bei bestimmten Konfigurationsänderungen verhindern. Bei der Neuerstellung von Activity werden die gesamte UI und alle aus Activity abgeleiteten Objekte neu erstellt. 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 es eine Leistungsbeschränkung gibt. In diesem Fall können Sie erklären, dass Ihre Aktivität die Konfigurationsänderung selbst durchführt und das System daran hindert, Ihre Aktivität neu zu starten.

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

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. Die in Android 12L (API-Level 32) eingeführte dynamische Farbänderung kann beispielsweise nicht deaktiviert werden.

Auf Konfigurationsänderungen im Ansichtssystem reagieren

Wenn im View-System eine Konfigurationsänderung auftritt, für die Sie die Neuerstellung von Activity deaktiviert haben, erhält die Aktivität einen Aufruf an Activity.onConfigurationChanged(). Alle angehängten Ansichten empfangen 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 Objekt Configuration, um die neue Konfiguration zu ermitteln. Aktualisieren Sie die Ressourcen, die Sie in der Benutzeroberfläche verwenden, um spätere Änderungen vorzunehmen. Wenn das System diese Methode aufruft, wird das Resources-Objekt Ihrer Aktivität aktualisiert, um Ressourcen auf der Grundlage der neuen Konfiguration zurückzugeben. Auf diese Weise 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 anhand dieser Konfigurationsänderungen nicht aktualisieren müssen, können Sie onConfigurationChanged() stattdessen 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. Beispielsweise möchte eine TV-App möglicherweise nicht reagieren, wenn eine Bluetooth-Tastatur angebracht oder entfernt wird.

Beibehaltungsstatus

Wenn Sie diese Technik verwenden, müssen Sie den Status während des normalen Aktivitätslebenszyklus trotzdem beibehalten. Das hat folgende Gründe:

  • Unvermeidliche Änderungen: Konfigurationsänderungen, die Sie nicht verhindern können, können die Anwendung neu starten.
  • Prozessabschluss: Ihre Anwendung muss in der Lage sein, den vom System initiierten Prozess zu verarbeiten. Wenn der Nutzer Ihre Anwendung verlässt und sie in den Hintergrund wechselt, zerstört das System sie möglicherweise.

In Jetpack Compose auf Konfigurationsänderungen reagieren

Mit Jetpack Compose kann Ihre App einfacher auf Konfigurationsänderungen reagieren. Wenn Sie die Wiederherstellung mit Activity jedoch für alle Konfigurationsänderungen deaktivieren, wo dies möglich ist, muss Ihre Anwendung Konfigurationsänderungen dennoch 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, neu zusammengesetzt. Informationen dazu, wie lokale Kompositionen funktionieren, finden Sie unter Daten auf lokaler Ebene 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 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))
    )
}

Damit Activity bei einer Änderung der Sprache nicht neu erstellt wird, muss die Activity, in der der Editor-Code gehostet wird, Ä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 UI dem Nutzer angezeigt wird, z. B. die Anzeigegröße der App, die Sprache oder das Systemdesign.
  • Konfigurationsänderungen:Konfigurationen ändern sich durch Nutzerinteraktionen. Beispielsweise kann der Nutzer 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 zur Neuinitialisierung des App-Status für die neue Konfiguration.
  • Löschen durch Activity: Durch die Neuerstellung von Activity löscht das System die alte Activity-Instanz und erstellt stattdessen eine neue Instanz. Die alte Instanz ist jetzt veraltet. Alle verbleibenden Verweise darauf können zu Speicherlecks, Programmfehlern oder Abstürzen führen.
  • State: Der Status in der alten Activity-Instanz ist in der neuen Activity-Instanz nicht vorhanden, da es sich um zwei verschiedene Objektinstanzen handelt. Behalten Sie den Status der Anwendung und des Nutzers wie unter UI-Status speichern beschrieben bei.
  • Deaktivieren: Das Deaktivieren der Wiederherstellung von Aktivitäten für eine Art von Konfigurationsänderung ist eine potenzielle Optimierung. Dazu muss Ihre Anwendung als Reaktion auf die neue Konfiguration ordnungsgemäß aktualisiert werden.

Beachten Sie die folgenden Best Practices, um für eine positive Nutzererfahrung zu sorgen:

  • Auf häufige Konfigurationsänderungen vorbereitet sein:Gehen Sie nicht davon aus, dass Konfigurationsänderungen selten oder nie erfolgen, unabhängig von API-Level, Formfaktor oder UI-Toolkit. Wenn ein Nutzer eine Konfigurationsänderung verursacht, erwartet er, dass Anwendungen aktualisiert werden und weiterhin korrekt mit der neuen Konfiguration funktionieren.
  • Status beibehalten:Der Status des Nutzers bleibt erhalten, wenn die Activity-Neuerstellung erfolgt. Behalten Sie den Status wie unter UI-Status speichern beschrieben bei.
  • Deaktivieren Sie die Wiederherstellung nicht als schnelle Lösung:Deaktivieren Sie nicht die Wiederherstellung von Activity als Verknüpfung, um den Verlust des Status zu vermeiden. Wenn Sie die Wiederherstellung von Aktivitäten deaktivieren, müssen Sie das Versprechen zur Verarbeitung der Änderung erfüllen. Sie können den Status dennoch verlieren, wenn Activity aufgrund anderer Konfigurationsänderungen neu erstellt wird, ein Fehler auftritt oder die App geschlossen wird. Die Neuerstellung von Activity kann nicht vollständig deaktiviert werden. Behalten Sie den Status wie unter UI-Status speichern beschrieben bei.
  • Konfigurationsänderungen nicht vermeiden:Richten Sie keine Einschränkungen in Bezug auf die Ausrichtung, das Seitenverhältnis oder die Größe ein, um Konfigurationsänderungen und die Neuerstellung von Activity zu vermeiden. Dies wirkt sich negativ auf Nutzer aus, die deine App auf ihre bevorzugte Weise verwenden möchten.

Größenbasierte Konfigurationsänderungen verarbeiten

Größenbasierte Konfigurationsänderungen können jederzeit vorgenommen werden. Dies ist wahrscheinlicher, wenn Ihre App auf einem Gerät mit großem Bildschirm 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 unerheblich. Eine erhebliche Größenänderung liegt vor, wenn aufgrund einer unterschiedlichen Bildschirmgröße, z. B. bei Breite, Höhe oder kleinster Breite, ein anderer Satz alternativer Ressourcen für die neue Konfiguration gilt. Zu diesen Ressourcen gehören Ressourcen, die von der App selbst definiert werden, und Ressourcen aus beliebigen Bibliotheken.

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

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

Die Wiederherstellung 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 wird Activity nur dann bei größenbasierten Konfigurationsänderungen neu erstellt, wenn die Größenänderung signifikant ist. Wenn das System ein Activity aufgrund unzureichender Größe nicht neu erstellt, ruft das System stattdessen möglicherweise Activity.onConfigurationChanged() und View.onConfigurationChanged() auf.

Es gibt einige Vorbehalte in Bezug auf die Activity- und View-Callbacks, wenn das 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 in 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 den späteren Versionen von Android 13 und Android 14 behoben.

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