Obsługa zmian konfiguracji

Niektóre konfiguracje urządzenia mogą się zmieniać podczas działania aplikacji. Obejmują one między innymi:

  • rozmiar interfejsu aplikacji,
  • orientację ekranu,
  • rozmiar i grubość czcionki,
  • język,
  • tryb ciemny i jasny,
  • dostępność klawiatury.

Większość tych zmian konfiguracji następuje w wyniku interakcji użytkownika. Na przykład obrócenie lub złożenie urządzenia zmienia ilość miejsca na ekranie dostępnego dla aplikacji. Podobnie zmiana ustawień urządzenia, takich jak rozmiar czcionki, język lub preferowany motyw, zmienia odpowiednie wartości w Configuration obiekcie.

Te parametry zwykle wymagają na tyle dużych zmian w interfejsie aplikacji, że platforma Android ma specjalny mechanizm, który jest uruchamiany, gdy się zmieniają. Ten mechanizm to odtworzenie Activity.

Odtworzenie `Activity`

Gdy nastąpi zmiana konfiguracji, system odtworzy Activity. W tym celu wywoła onDestroy i zniszczy istniejącą Activityinstancję. Następnie utworzy nową instancję za pomocą onCreate, a ta nowa Activity instancja zostanie zainicjowana z nową, zaktualizowaną konfiguracją. Oznacza to również, że system odtworzy interfejs z nową konfiguracją.

Zwykle Activity działa jako host dla funkcji kompozycyjnych. Gdy Activity zostanie odtworzona, Compose odtworzy też interfejs z użyciem nowych wartości konfiguracji.

Dzięki odtworzeniu aplikacja może dostosować się do nowych konfiguracji, automatycznie wczytując alternatywne zasoby pasujące do nowej konfiguracji urządzenia.

Przykład odtworzenia

Rozważmy funkcję kompozycyjną, która wyświetla statyczny tytuł za pomocą zasobu tekstowego:

// In the res/values/strings.xml file
// <string name="compose">Jetpack Compose</string>

// In your Compose code
Text(
    text = stringResource(R.string.compose)
)

Gdy Activity zostanie utworzona, funkcja kompozycyjna Text odczyta bieżącą konfigurację (np. język) i rozwiąże odpowiedni zasób tekstowy.

Jeśli język się zmieni, system odtworzy aktywność. Gdy to nastąpi, Compose odtworzy interfejs. Ponieważ stringResource odczytuje dane z bieżącej konfiguracji, tytuł automatycznie zaktualizuje się do prawidłowej wartości zlokalizowanej.

Odtworzenie spowoduje też wyczyszczenie stanu przechowywanego jako pola w Activity.

Aby zachować stan interfejsu po zmianach konfiguracji, użyj zalecanych wzorców zarządzania stanem. Użyj ViewModel do danych i logiki biznesowej oraz rememberSaveable do stanu na poziomie interfejsu. Dzięki tym mechanizmom stan przetrwa odtworzenie Activity, a interfejs zaktualizuje się, aby odzwierciedlić nową konfigurację.

Więcej informacji o zapisywaniu stanu w Compose znajdziesz w artykule Zapisywanie stanu interfejsu w Compose.

Oczekiwania użytkowników

Użytkownik aplikacji oczekuje, że stan zostanie zachowany. Jeśli użytkownik wypełnia formularz i otwiera inną aplikację w trybie wielu okien, aby sprawdzić informacje, to jeśli po powrocie do formularza okaże się, że jest on pusty, lub jeśli użytkownik zostanie przeniesiony w inne miejsce w aplikacji, będzie to złe wrażenie. Jako deweloper musisz zapewnić spójne wrażenia użytkownika podczas zmian konfiguracji i odtwarzania aktywności.

Aby sprawdzić, czy stan jest zachowywany w aplikacji, możesz wykonywać działania powodujące zmiany konfiguracji zarówno wtedy, gdy aplikacja jest na pierwszym planie, jak i wtedy, gdy działa w tle. Są to między innymi następujące działania:

  • obracanie urządzenia,
  • włączanie trybu wielu okien,
  • zmiana rozmiaru aplikacji w trybie wielu okien lub w oknie o dowolnym kształcie,
  • składanie urządzenia składanego z wieloma ekranami,
  • zmiana motywu systemu, np. z trybu ciemnego na jasny,
  • zmiana rozmiaru czcionki,
  • zmiana języka systemu lub aplikacji,
  • podłączanie i odłączanie klawiatury sprzętowej,
  • podłączanie i odłączanie stacji dokującej.

Istnieje kilka sposobów na zachowanie odpowiedniego stanu podczas odtwarzania Activity. To, którego z nich użyć, zależy od typu stanu, który chcesz zachować:

  • Lokalne utrwalanie danych w celu obsługi śmierci procesu w przypadku złożonych lub dużych danych. Trwałe przechowywanie lokalne obejmuje bazy danych lub DataStore.
  • Obiekty zachowywane, takie jak ViewModel instancje do obsługi stanu związanego z interfejsem w pamięci, gdy użytkownik aktywnie korzysta z aplikacji.
  • rememberSaveable do zachowania tymczasowego stanu interfejsu po zmianach konfiguracji i śmierci procesu zainicjowanej przez system. Jest to odpowiednie w przypadku stanu, który zależy od danych wejściowych użytkownika, pozycji przewijania lub nawigacji, ale nie należy do ViewModel.

Aby dowiedzieć się więcej o interfejsach API dla każdego z tych elementów, i o tym, kiedy należy ich używać, przeczytaj artykuł Zapisywanie stanów interfejsu.

Ograniczanie odtwarzania aktywności

Możesz zapobiec automatycznemu odtwarzaniu aktywności w przypadku niektórych zmian konfiguracji. W nowoczesnych aplikacjach, które korzystają tylko z Compose, interfejs jest i tak ponownie komponowany, ale zalecamy bezpośrednie obsługiwanie zmian konfiguracji.

Domyślnie zmiana konfiguracji powoduje, że system niszczy i odtwarza aktywność, w tym interfejs i wszystkie obiekty pochodne aktywności. Jeśli zadeklarujesz, że aktywność sama obsługuje zmianę konfiguracji, system temu zapobiegnie. Zamiast tego zaktualizuje się tylko obiekt Configuration, a Compose ponownie skomponuje interfejs z nowymi wartościami.

Bezpośrednie obsługiwanie zmian konfiguracji w Compose ma kilka zalet:

  • Lepsza wydajność: ponowne komponowanie interfejsu jest mniej kosztowne niż pełny cykl odtwarzania aktywności, zwłaszcza w przypadku drobnych zmian.
  • Płynne animacje: unikanie ponownego uruchamiania aktywności pozwala na ciągłe animacje podczas zmian konfiguracji, np. płynne przejścia układu podczas obracania urządzenia.
  • Zachowanie stanu: Zachowanie instancji Activity zmniejsza ryzyko utraty tymczasowego stanu interfejsu podczas zdarzenia, takiego jak obracanie ekranu. Pamiętaj, że nadal musisz obsługiwać zachowanie stanu w przypadku śmierci procesu zainicjowanego przez system.

Aby wyłączyć odtwarzanie aktywności w przypadku określonych zmian konfiguracji, dodaj typ konfiguracji do android:configChanges w <activity> w pliku AndroidManifest.xml. Możliwe wartości znajdziesz w dokumentacji atrybutu android:configChanges.

Ten kod manifestu wyłącza Activity odtwarzanie dla MyActivity, gdy zmieni się orientacja ekranu i dostępność klawiatury:

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

Reagowanie na zmiany konfiguracji

Jetpack Compose ułatwia aplikacji reagowanie na zmiany konfiguracji. Jeśli jednak wyłączysz odtwarzanie Activity w przypadku wszystkich zmian konfiguracji, w których jest to możliwe, aplikacja nadal musi prawidłowo obsługiwać zmiany konfiguracji.

Obiekt Configuration jest dostępny w hierarchii interfejsu Compose za pomocą lokalnego składu LocalConfiguration. Za każdym razem, gdy się zmieni, funkcje kompozycyjne odczytujące dane z LocalConfiguration.current zostaną ponownie skomponowane. Więcej informacji o tym, jak działają lokalne składy, znajdziesz w artykule Dane o zasięgu lokalnym za pomocą CompositionLocal.

Przykład

W tym przykładzie funkcja kompozycyjna wyświetla datę w określonym formacie. Funkcja kompozycyjna reaguje na zmiany konfiguracji ustawień regionalnych systemu, wywołując ConfigurationCompat.getLocales z 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))
    )
}

Aby uniknąć odtworzenia Activity po zmianie ustawień regionalnych, Activity hostująca kod Compose musi zrezygnować ze zmian konfiguracji ustawień regionalnych. Aby to zrobić, ustaw android:configChanges na locale|layoutDirection.

Zmiany konfiguracji: najważniejsze pojęcia i sprawdzone metody

Oto najważniejsze pojęcia, które musisz znać, gdy pracujesz nad zmianami konfiguracji:

  • Konfiguracje: konfiguracje urządzenia określają, jak interfejs jest wyświetlany użytkownikowi, np. rozmiar interfejsu aplikacji, ustawienia regionalne lub motyw systemu. W Compose możesz uzyskać dostęp do wartości konfiguracji za pomocą LocalConfiguration.
  • Zmiany konfiguracji: konfiguracje zmieniają się w wyniku interakcji użytkownika. Użytkownik może na przykład zmienić ustawienia urządzenia lub sposób fizycznej interakcji z urządzeniem. Nie można zapobiec zmianom konfiguracji.
  • Odtworzenie Activity: zmiany konfiguracji domyślnie powodują odtworzenie Activity. Jest to wbudowany mechanizm ponownego inicjowania stanu aplikacji dla nowej konfiguracji.
  • Zniszczenie Activity: odtworzenie Activity powoduje, że system niszczy starą instancję Activity i tworzy w jej miejsce nową. Stara instancja jest już przestarzała. Unikaj zachowywania odwołań do obiektów o zasięgu cyklu życia poza ich zamierzonym zakresem.
  • Stan: stan w starej instancji Activity nie jest obecny w nowej instancji Activity, ponieważ są to 2 różne instancje obiektu. Zamiast wiązać stan z aktywnością, używaj zalecanych interfejsów API, aby zachować stan aplikacji i użytkownika zgodnie z opisem w artykule Zapisywanie stanów interfejsu.
  • Rezygnacja: rezygnacja z odtwarzania aktywności w przypadku zmiany konfiguracji wymaga, aby aplikacja prawidłowo się zaktualizowała w reakcji na nową konfigurację. W przypadku większości aplikacji Compose nie jest to zalecane.

Aby zapewnić użytkownikom dobre wrażenia, stosuj te sprawdzone metody:

  • Przygotuj się na częste zmiany konfiguracji: nie zakładaj, że zmiany konfiguracji są rzadkie lub nigdy nie występują, niezależnie od poziomu interfejsu API, formatu urządzenia czy zestawu narzędzi interfejsu. Gdy użytkownik spowoduje zmianę konfiguracji, oczekuje, że aplikacje się zaktualizują i będą nadal działać prawidłowo z nową konfiguracją.
  • Zachowaj stan: nie trać stanu użytkownika, gdy nastąpi odtworzenie Activity. Zachowaj stan zgodnie z opisem w Zapisywanie stanów interfejsu za pomocą interfejsów API takich jak ViewModel i rememberSaveable.
  • Unikaj rezygnacji jako szybkiego rozwiązania: nie rezygnuj z odtwarzania Activity jako skrótu, aby uniknąć utraty stanu. Rezygnacja z odtwarzania aktywności wymaga, abyś dotrzymał obietnicy obsługi zmiany, a stan nadal może zostać utracony z powodu odtworzenia Activity przez inne zmiany konfiguracji, zakończenie procesu lub zamknięcie aplikacji. Nie można całkowicie wyłączyć odtwarzania Activity. Zachowaj stan zgodnie z opisem w artykule Zapisywanie stanów interfejsu.
  • Nie unikaj zmian konfiguracji: nie ograniczaj orientacji, proporcji ani możliwości zmiany rozmiaru, aby uniknąć zmian konfiguracji i Activity odtwarzania. Ma to negatywny wpływ na użytkowników, którzy chcą korzystać z aplikacji w preferowany przez siebie sposób.

Obsługa zmian konfiguracji opartych na rozmiarze

Zmiany konfiguracji oparte na rozmiarze mogą nastąpić w dowolnym momencie i są bardziej prawdopodobne , gdy aplikacja działa na urządzeniu z dużym ekranem, na którym użytkownicy mogą włączyć tryb wielu okien. Oczekują, że aplikacja będzie dobrze działać w tym środowisku.

Istnieją 2 ogólne typy zmian rozmiaru: znaczące i nieznaczące. Znacząca zmiana rozmiaru to taka, w której do nowej konfiguracji stosuje się inny zestaw alternatywnych zasobów ze względu na różnicę w rozmiarze ekranu, np. szerokości, wysokości lub najmniejszej szerokości. Te zasoby obejmują te, które definiuje sama aplikacja, oraz te z dowolnej jej biblioteki.

Ograniczanie odtwarzania aktywności w przypadku zmian konfiguracji opartych na rozmiarze

Gdy wyłączysz odtwarzanie Activity w przypadku zmian konfiguracji opartych na rozmiarze, system nie odtworzy Activity. Zamiast tego otrzyma wywołanie Activity.onConfigurationChanged. Wszystkie funkcje kompozycyjne odczytujące dane z LocalConfiguration.current zostaną automatycznie ponownie skomponowane, aby odzwierciedlić nowy rozmiar.

Activity odtwarzanie jest wyłączone w przypadku zmian konfiguracji opartych na rozmiarze, gdy masz android:configChanges="screenSize|smallestScreenSize|orientation|screenLayout" w pliku manifestu.

Dodatkowe materiały

Więcej informacji o obsłudze zmian konfiguracji znajdziesz w tych dodatkowych materiałach:

Dokumentacja

Wyświetlanie treści