Niektóre konfiguracje urządzenia mogą się zmieniać podczas działania aplikacji. Obejmują one m.in.:
- Rozmiar interfejsu aplikacji
- Gęstość ekranu (skalowanie wyświetlacza)
- Orientacja ekranu
- Rozmiar i grubość czcionki
- Język
- Ciemny motyw a jasny motyw
- Dostępność klawiatury
Większość tych zmian konfiguracji następuje w wyniku interakcji użytkownika. Na przykład obracanie lub składanie urządzenia zmienia ilość miejsca na ekranie dostępnego dla aplikacji. Podłączenie do wyświetlaczy zewnętrznych lub przeniesienie aplikacji między wyświetlaczami o różnej gęstości pikseli zmienia gęstość ekranu i rozmiar wyświetlacza. Podobnie zmiana ustawień urządzenia, takich jak skalowanie wyświetlacza, rozmiar czcionki, język czy preferowany motyw, powoduje zmianę odpowiednich wartości w obiekcie Configuration.
Te parametry zwykle wymagają na tyle dużych zmian w interfejsie aplikacji, że platforma Androida ma specjalny mechanizm, który reaguje na ich zmianę.
Ten mechanizm to ponowne utworzenie Activity.
Rekreacja
System odtwarza Activity, gdy następuje zmiana konfiguracji. W tym celu system wywołuje metodę onDestroy i niszczy istniejącą instancję Activity. Następnie tworzy nową instancję za pomocą onCreate, a ta nowa instancja Activity jest inicjowana z nową, zaktualizowaną konfiguracją. Oznacza to również, że system odtwarza interfejs z nową konfiguracją.
Zwykle Activity działa jako host dla funkcji kompozycyjnych. Gdy Activity zostanie utworzona ponownie, Compose również odtworzy interfejs, używając nowych wartości konfiguracji.
Zachowanie polegające na ponownym tworzeniu pomaga aplikacji dostosować się do nowych konfiguracji przez automatyczne ponowne wczytanie aplikacji z zasobami alternatywnymi, które pasują do nowej konfiguracji urządzenia.
Przykład dotyczący rekreacji
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) )
Po utworzeniu Activity funkcja kompozycyjna Text odczytuje bieżącą konfigurację (np. język) i rozpoznaje odpowiedni zasób tekstowy.
Jeśli język się zmieni, system ponownie utworzy aktywność. W takim przypadku Compose odtwarza interfejs. Ponieważ stringResource odczytuje dane z bieżącej konfiguracji, tytuł automatycznie aktualizuje się do prawidłowej zlokalizowanej wartości.
Ponowne utworzenie powoduje też wyczyszczenie wszystkich stanów przechowywanych jako pola w Activity.
Aby zachować stan interfejsu podczas zmian konfiguracji, użyj zalecanych wzorców zarządzania stanem. Używaj ViewModel do danych i logiki biznesowej, a rememberSaveable do stanu na poziomie interfejsu. Dzięki tym mechanizmom stan przetrwa odtworzenie Activity, a interfejs zostanie zaktualizowany, 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, powrót do wyczyszczonego formularza lub do zupełnie innego miejsca w aplikacji będzie dla niego nieprzyjemnym doświadczeniem. Jako deweloper musisz zapewnić spójne wrażenia użytkownika przez zmiany konfiguracji i ponowne tworzenie aktywności.
Aby sprawdzić, czy stan jest zachowywany w aplikacji, możesz wykonać działania, które powodują zmiany konfiguracji, gdy aplikacja działa na pierwszym planie i w tle. Dotyczy to następujących czynności:
- obracanie urządzenia,
- Włączanie trybu wielu okien
- zmiana rozmiaru aplikacji w trybie wielu okien lub w oknie swobodnym;
- składanie urządzenia składanego z wieloma wyświetlaczami,
- zmiana rozmiaru interfejsu lub skalowania wyświetlacza w ustawieniach systemowych,
- Przenoszenie aplikacji między wyświetlaczami o różnej gęstości ekranu (np. podłączonymi monitorami zewnętrznymi lub wyświetlaczami urządzenia składanego).
- zmiana motywu systemowego, np. z ciemnego motywu na jasny motyw;
- Zmiana rozmiaru czcionki
- Zmiana języka systemu lub aplikacji
- podłączanie lub odłączanie klawiatury sprzętowej,
- podłączanie lub odłączanie stacji dokującej,
Istnieje kilka sposobów na zachowanie odpowiedniego stanu podczas ponownego tworzenia Activity. To, którego z nich użyć, zależy od typu stanu, który chcesz zachować:
- Lokalne przechowywanie danych, aby obsługiwać śmierć procesu w przypadku złożonych lub dużych danych.
Trwałe przechowywanie lokalne obejmuje bazy danych lub
DataStore. - Obiekty zachowane, takie jak instancje
ViewModel, do obsługi stanu związanego z interfejsem w pamięci, gdy użytkownik aktywnie korzysta z aplikacji. rememberSaveable, aby zachować stan przejściowy interfejsu użytkownika podczas zmian 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 doViewModel.
Szczegółowe informacje o interfejsach API dla każdego z tych rozwiązań oraz o tym, kiedy należy ich używać, znajdziesz w artykule Zapisywanie stanów interfejsu.
Ograniczanie ponownego tworzenia aktywności
Możesz zapobiec automatycznemu odtwarzaniu aktywności w przypadku niektórych zmian konfiguracji. W nowoczesnych aplikacjach korzystających tylko z Compose interfejs i tak jest rekomponowany, ale zalecamy bezpośrednią obsługę zmiany konfiguracji.
Domyślnie zmiana konfiguracji wymusza na systemie zniszczenie i ponowne utworzenie aktywności, w tym interfejsu i wszystkich obiektów pochodzących z aktywności. Jeśli zadeklarujesz, że aktywność sama obsługuje zmianę konfiguracji, system temu zapobiegnie. Zamiast tego aktualizowany jest tylko obiekt Configuration, a Compose ponownie tworzy interfejs z nowymi wartościami.
Obsługa zmian konfiguracji bezpośrednio 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 odtwarzanie animacji podczas zmian konfiguracji, np. płynne przejścia układu podczas obracania urządzenia.
- Zachowanie stanu: zachowanie instancji aktywności zmniejsza ryzyko utraty tymczasowego stanu interfejsu podczas zdarzenia takiego jak obrót ekranu. Pamiętaj, że nadal musisz obsługiwać zachowywanie stanu w przypadku śmierci procesu zainicjowanej przez system.
Aby wyłączyć ponowne tworzenie aktywności w przypadku określonych zmian konfiguracji, dodaj typ konfiguracji do android:configChanges we wpisie <activity> w pliku AndroidManifest.xml. Możliwe wartości znajdziesz w dokumentacji atrybutu android:configChanges.
Poniższy kod pliku manifestu wyłącza ponowne tworzenie Activity dla MyActivity, gdy zmienia się orientacja ekranu i dostępność klawiatury:
<activity
android:name=".MyActivity"
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"
android:label="@string/app_name">
Android 17
Od Androida 17 (poziom API 37) system nie uruchamia już domyślnie ponownie aktywności w przypadku kilku zmian konfiguracji, które zwykle nie wymagają pełnego odtworzenia interfejsu.
Zamiast tego aktywności pozostają aktywne i otrzymują aktualizacje przez wywołanie zwrotne onConfigurationChanged().
Konkretne zmiany konfiguracji obejmują:
CONFIG_KEYBOARDCONFIG_KEYBOARD_HIDDENCONFIG_NAVIGATIONCONFIG_TOUCHSCREENCONFIG_COLOR_MODECONFIG_UI_MODE(tylko gdy tryb interfejsu zmienia się naUI_MODE_TYPE_DESKlub zUI_MODE_TYPE_DESKna inny typ)
Jeśli Twoja aplikacja wymaga pełnego ponownego uruchomienia, aby ponownie wczytać zasoby po tych zmianach, musisz teraz wyraźnie zaakceptować stare działanie, używając atrybutu android:recreateOnConfigChanges w pliku manifestu.
Atrybut pozwala określić, które zmiany konfiguracji powinny wywołać pełny cykl zatrzymania, zniszczenia i ponownego utworzenia aktywności, na przykład:
<activity
android:name=".MyActivity"
android:recreateOnConfigChanges="keyboard|keyboardHidden|navigation|colorMode|touchscreen|...">
...
</activity>
Reagowanie na zmiany konfiguracji
Jetpack Compose ułatwia aplikacji reagowanie na zmiany konfiguracji.
Jeśli jednak wyłączysz ponowne tworzenie 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 z lokalną kompozycją LocalConfiguration. Za każdym razem, gdy się zmienia, funkcje typu „composable” odczytujące dane z LocalConfiguration.current są ponownie tworzone. Więcej informacji o działaniu zmiennych lokalnych kompozycji znajdziesz w artykule Dane o zakresie lokalnym z CompositionLocal.
Przykład
W poniższym przykładzie funkcja kompozycyjna wyświetla datę w określonym formacie.
Funkcja kompozycyjna reaguje na zmiany konfiguracji języka systemu, wywołując funkcję ConfigurationCompat.getLocales z parametrem 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ąć ponownego tworzenia Activity po zmianie języka, Activity, który hostuje kod Compose, musi zrezygnować ze zmian konfiguracji języka. W tym celu ustaw android:configChanges na locale|layoutDirection.
Zmiany konfiguracji: kluczowe 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 wyświetla się użytkownikowi, np. rozmiar wyświetlania aplikacji, gęstość ekranu, region lub motyw systemowy.
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 nim. Nie można zapobiec zmianom konfiguracji.
- Odtwarzanie
Activity: zmiany konfiguracji domyślnie powodują odtworzenieActivity. Jest to wbudowany mechanizm ponownego inicjowania stanu aplikacji dla nowej konfiguracji. - Zniszczenie
Activity: odtworzenieActivitypowoduje, że system niszczy starą instancjęActivityi tworzy nową na jej miejscu. Stara instancja jest teraz nieaktualna. Unikaj zachowywania odwołań do obiektów o określonym cyklu życia poza ich zamierzonym zakresem. - Stan: stan w starej instancji
Activitynie jest obecny w nowej instancjiActivity, ponieważ są to 2 różne instancje obiektów. Zamiast wiązać stan z aktywnością, użyj zalecanych interfejsów API, aby zachować stan aplikacji i użytkownika zgodnie z opisem w artykule Zapisywanie stanów interfejsu. - Rezygnacja: rezygnacja z ponownego tworzenia aktywności w przypadku zmiany typu konfiguracji wymaga, aby aplikacja prawidłowo aktualizowała się w reakcji na nową konfigurację.
Aby zapewnić użytkownikom wygodę, 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 czy zestawu narzędzi interfejsu. Gdy użytkownik wprowadza zmianę konfiguracji, oczekuje, że aplikacje zostaną zaktualizowane i będą nadal działać prawidłowo z nową konfiguracją.
- Zachowywanie stanu: nie tracisz stanu użytkownika, gdy
Activityjest odtwarzany. Zachowaj stan zgodnie z opisem w artykule Zapisywanie stanów interfejsu, używając interfejsów API takich jakViewModelirememberSaveable. - Unikaj rezygnacji jako szybkiego rozwiązania: nie rezygnuj z ponownego tworzenia
Activity, aby uniknąć utraty stanu. Rezygnacja z ponownego tworzenia aktywności wymaga od Ciebie obsługi zmiany. Nadal możesz utracić stan z powodu ponownego utworzeniaActivityw wyniku innych zmian konfiguracji, śmierci procesu lub zamknięcia aplikacji. Nie można całkowicie wyłączyć ponownego tworzeniaActivity. Zachowaj stan zgodnie z opisem w sekcji Zapisywanie stanów interfejsu. - Nie unikaj zmian konfiguracji: nie nakładaj ograniczeń na orientację, format obrazu ani możliwość zmiany rozmiaru, aby uniknąć zmian konfiguracji i ponownego tworzenia
Activity. 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 na podstawie rozmiaru
Zmiany konfiguracji związane z rozmiarem mogą wystąpić w każdej chwili 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 Twoja aplikacja będzie dobrze działać w tym środowisku.
Istnieją 2 ogólne typy zmian rozmiaru: istotne i nieistotne. Znaczna zmiana rozmiaru to taka, w której do nowej konfiguracji ma zastosowanie inny zestaw zasobów alternatywnych ze względu na różnicę w rozmiarze ekranu, np. szerokość, wysokość lub najmniejszą szerokość. Zasoby te obejmują te, które aplikacja definiuje sama, oraz te z jej bibliotek.
Ograniczanie ponownego tworzenia aktywności w przypadku zmian konfiguracji związanych z rozmiarem
Gdy wyłączysz ponowne tworzenie Activity w przypadku zmian konfiguracji związanych z rozmiarem, system nie utworzy ponownie Activity. Zamiast tego otrzymuje wywołanie Activity.onConfigurationChanged. Wszystkie elementy kompozycyjne odczytujące LocalConfiguration.current automatycznie zmieniają kompozycję, aby odzwierciedlić nowy rozmiar.
Odtwarzanie Activity jest wyłączone w przypadku zmian konfiguracji związanych z rozmiarem, gdy w pliku manifestu masz android:configChanges="screenSize|smallestScreenSize|orientation|screenLayout".
Dodatkowe materiały
Więcej informacji o obsłudze zmian konfiguracji znajdziesz w tych materiałach: