Niektóre konfiguracje urządzeń mogą się zmieniać, gdy aplikacja jest uruchomiona. Obejmują one m.in.:
- Rozmiar interfejsu aplikacji
- Orientacja ekranu
- rozmiar i grubość czcionki,
- Język
- Tryb ciemny i jasny
- Dostępność klawiatury
Większość z tych zmian konfiguracji wynika z interakcji użytkownika. Na przykład obrócenie lub złożenie urządzenia zmienia ilość miejsca na ekranie dostępnego dla aplikacji. Analogicznie zmiana ustawień urządzenia, takich jak rozmiar czcionki, język czy preferowany motyw, powoduje zmianę odpowiednich wartości w obiekcie Configuration
.
Te parametry zwykle wymagają dużych zmian w interfejsie aplikacji, aby platforma Androida miała specjalny mechanizm umożliwiający działanie w razie zmiany.
Ten mechanizm to odtworzenie Activity
.
Rekreacja
Po zmianie konfiguracji system odtwarza Activity
. W tym celu system wywołuje metodę onDestroy()
i niszczy istniejącą instancję Activity
. Następnie tworzy nową instancję z użyciem onCreate()
, która jest inicjowana Activity
z nową, zaktualizowaną konfiguracją. Oznacza to również, że system odtworzy interfejs użytkownika z nową konfiguracją.
Pozwala to aplikacji dostosować się do nowych konfiguracji przez automatyczne ponowne załadowanie aplikacji przy użyciu alternatywnych zasobów pasujących do nowej konfiguracji urządzenia.
Przykład dotyczący rekreacji
Weźmy na przykład TextView
, który wyświetla statyczny tytuł z użyciem android:text="@string/title"
, zgodnie z definicją w pliku XML układu. Podczas tworzenia widoku tekst jest ustawiany dokładnie raz na podstawie bieżącego języka. Jeśli język się zmieni, system odtworzy aktywność. W rezultacie system ponownie odtworzy widok i zainicjuje go do prawidłowej wartości na podstawie nowego języka.
Odtworzenie usuwa też każdy stan zachowany jako pola w Activity
lub dowolnych zawartych w nim obiektach Fragment
, View
lub innych. Dzieje się tak, ponieważ odtworzenie Activity
powoduje utworzenie zupełnie nowej instancji interfejsu Activity
i interfejsu. Poza tym stary element Activity
nie jest już widoczny ani prawidłowy, więc wszystkie pozostałe odwołania do niego lub jego obiektów będą nieaktualne. Mogą powodować błędy,
wycieki pamięci i awarie.
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 zapoznać się z informacjami, może to negatywnie wpłynąć na jego wrażenia, gdy wróci do nieczytelnego formularza lub znajdzie się w innym miejscu w aplikacji. Jako deweloper musisz zapewnić spójne wrażenia użytkowników, zmieniając konfigurację i przywracając aktywność.
Aby sprawdzić, czy stan aplikacji jest zachowywany, możesz wykonać działania, które powodują zmiany konfiguracji zarówno wtedy, gdy aplikacja działa na pierwszym planie, jak i gdy działa w tle. Dotyczy to następujących czynności:
- Obracanie urządzenia
- Otwieram tryb wielu okien
- Zmiana rozmiaru aplikacji w trybie wielu okien lub w dowolnym oknie
- Składanie składanego urządzenia z kilkoma wyświetlaczami
- zmienianie motywu systemowego, np. trybu ciemnego lub jasnego;
- 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
Istnieją 3 główne podejścia, które możesz podjąć, aby zachować odpowiedni stan przez ponowne odtworzenie Activity
. Wybór sposobu zależy od rodzaju stanu, który chcesz zachować:
- Trwałość lokalna w przypadku śmierci procesu w przypadku złożonych lub dużych danych.
Stała pamięć lokalna obejmuje bazy danych lub
DataStore
. - Przechowywane obiekty, takie jak instancje
ViewModel
, służące do obsługi stanu związanego z interfejsem w pamięci, gdy użytkownik aktywnie korzysta z aplikacji. - Zapisany stan instancji na potrzeby obsługi zakończenia procesu inicjowanego przez system i zachowania stanu przejściowego, który zależy od danych wejściowych lub nawigacji użytkownika.
Aby dowiedzieć się więcej o interfejsach API związanych z każdym z tych interfejsów i o tym, kiedy warto z nich korzystać, przeczytaj artykuł Zapisywanie stanów interfejsu użytkownika.
Ogranicz odtwarzanie aktywności
Możesz uniemożliwić automatyczne odtwarzanie aktywności w przypadku niektórych zmian konfiguracji.
Odtworzenie Activity
powoduje odtworzenie całego interfejsu użytkownika i wszystkich obiektów, które pochodzą z Activity
. Może to być dobry powód, aby tego uniknąć. Na przykład aplikacja może nie wymagać aktualizacji zasobów w przypadku określonej zmiany konfiguracji lub obowiązują Cię ograniczenia wydajności. W takim przypadku możesz zadeklarować, że Twoja aktywność sama obsługuje zmianę konfiguracji i uniemożliwia systemowi jej ponowne uruchomienie.
Aby wyłączyć odtwarzanie 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
.
Ten kod manifestu wyłącza odtwarzanie Activity
dla MyActivity
po zmianie orientacji ekranu i dostępności klawiatury:
<activity
android:name=".MyActivity"
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"
android:label="@string/app_name">
Niektóre zmiany w konfiguracji zawsze powodują ponowne uruchomienie aktywności. Nie można ich wyłączyć. Nie możesz na przykład wyłączyć dynamicznej zmiany kolorów wprowadzonej w Androidzie 12L (poziom interfejsu API 32).
Reagowanie na zmiany konfiguracji w systemie wyświetlania
Gdy w systemie View
zajdzie zmiana konfiguracji, w której wyłączysz odtwarzanie Activity
, aktywność otrzyma wywołanie Activity.onConfigurationChanged()
. Wszystkie dołączone wyświetlenia również będą wywoływać metodę View.onConfigurationChanged()
. W przypadku zmian w konfiguracji, które nie zostały dodane do funkcji android:configChanges
, system odtwarza działanie tak jak zwykle.
Metoda wywołania zwrotnego onConfigurationChanged()
otrzymuje obiekt Configuration
, który określa nową konfigurację urządzenia. Przeczytaj pola w obiekcie Configuration
, aby określić nową konfigurację. Aby wprowadzić kolejne zmiany, zaktualizuj zasoby, których używasz w interfejsie. Gdy system wywoła tę metodę, obiekt Resources
Twojej aktywności zostanie zaktualizowany tak, aby zwracał zasoby na podstawie nowej konfiguracji. Dzięki temu możesz zresetować elementy interfejsu bez ponownego uruchamiania aktywności przez system.
Na przykład ta implementacja onConfigurationChanged()
sprawdza, czy jest dostępna klawiatura:
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();
}
}
Jeśli nie musisz aktualizować aplikacji po wprowadzeniu tych zmian w konfiguracji, możesz zamiast tego wdrożyć onConfigurationChanged()
. W takim przypadku wszystkie zasoby używane przed zmianą konfiguracji będą nadal używane, a Ty udało Ci się tylko uniknąć ponownego uruchomienia aktywności. Na przykład aplikacja TV może nie reagować po podłączeniu lub odłączeniu klawiatury Bluetooth.
Zachowaj stan
Stosując tę metodę, musisz zachować stan w trakcie normalnego cyklu aktywności. Dzieje się tak, ponieważ:
- Nieuniknione zmiany: zmiany w konfiguracji, których nie możesz zapobiec, mogą spowodować ponowne uruchomienie aplikacji.
- śmierć procesu: aplikacja musi obsługiwać śmierć procesu inicjowanego przez system. Jeśli użytkownik opuści aplikację, a aplikacja będzie działać w tle, system może ją zniszczyć.
Reagowanie na zmiany konfiguracji w Jetpack Compose
Jetpack Compose pozwala Twojej aplikacji łatwiej reagować na zmiany w konfiguracji.
Jeśli jednak wyłączysz odtwarzanie Activity
w przypadku wszystkich zmian konfiguracji, gdy jest to możliwe, aplikacja nadal będzie prawidłowo obsługiwać te zmiany.
Obiekt Configuration
jest dostępny w hierarchii interfejsu tworzenia wiadomości z lokalną kompozycją LocalConfiguration
. Gdy tylko się zmieni, funkcje kompozycyjne odczytywane z pliku LocalConfiguration.current
tworzą ponownie. Informacje o lokalnym działaniu kompozycji znajdziesz w artykule o danych o zakresie lokalnym w kompozycji CompositionLocal.
Przykład
W poniższym przykładzie funkcja kompozycyjna wyświetla datę w określonym formacie.
Obiekt kompozycyjny reaguje na zmiany konfiguracji ustawień regionalnych systemu, wywołując metodę ConfigurationCompat.getLocales()
z użyciem polecenia 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ąć przywracania Activity
po zmianie języka, obiekt Activity
hostujący kod tworzenia wiadomości musi zrezygnować ze zmian konfiguracji języka. Aby to zrobić, ustaw android:configChanges
na locale|layoutDirection
.
Zmiany konfiguracji: kluczowe pojęcia i sprawdzone metody
Oto kluczowe zagadnienia, które musisz znać podczas pracy nad zmianami w konfiguracji:
- Konfiguracje: konfiguracje urządzeń określają, jak wyświetla się interfejs użytkownika, np. rozmiar interfejsu aplikacji, język czy motyw systemowy.
- Zmiany konfiguracji: konfiguracja zmienia się w wyniku interakcji użytkownika. Użytkownik może na przykład zmienić ustawienia urządzenia lub sposób korzystania z urządzenia. Nie da się zapobiec zmianom konfiguracji.
- Odtwarzanie
Activity
: zmiany w konfiguracji powodują domyślnie ponowne odtworzenieActivity
. To jest wbudowany mechanizm ponownego inicjowania stanu aplikacji na potrzeby nowej konfiguracji. - Zniszczenie obiektu
Activity
: odtwarzanieActivity
powoduje, że system niszczy starą instancjęActivity
i w jej miejsce tworzy nową. Stara instancja jest teraz przestarzała. Wszelkie pozostałe odwołania do niej mogą spowodować wyciek pamięci, błędy lub awarie. - Stan: w starej instancji
Activity
nie ma stanu: w nowej instancjiActivity
, ponieważ są to 2 różne instancje obiektów. Zachowaj stan aplikacji i użytkownika zgodnie z opisem w sekcji Zapisywanie stanów interfejsu użytkownika. - Rezygnacja: rezygnacja z odtwarzania aktywności w przypadku zmiany konfiguracji jest potencjalną optymalizacją. Wymaga ona odpowiedniego aktualizowania aplikacji w odpowiedzi na nową konfigurację.
Aby zadbać o wygodę użytkowników, stosuj te sprawdzone metody:
- Przygotuj się na częste zmiany w konfiguracji: nie zakładaj, że zmiany w konfiguracji są rzadkie lub nigdy nie występują, niezależnie od poziomu interfejsu API, formatu czy zestawu narzędzi interfejsu. Gdy użytkownik powoduje zmianę konfiguracji, oczekuje, że aplikacje zostaną zaktualizowane i nadal będą prawidłowo działać zgodnie z nową konfiguracją.
- Zachowaj stan: nie utracisz stanu użytkownika, gdy rozpocznie się odtwarzanie
Activity
. Zachowaj stan zgodnie z opisem w sekcji Zapisywanie stanów interfejsu użytkownika. - Nie rezygnuj z tej funkcji w ramach szybkiego rozwiązania: nie rezygnuj z odtwarzania
Activity
w celu uniknięcia utraty stanu. Rezygnacja z odtwarzania aktywności wymaga spełnienia obietnicy wprowadzenia zmian, a nadal możesz utracić stan z powodu przywróceniaActivity
z powodu innych zmian konfiguracji, śmierci lub zamknięcia aplikacji. Nie można całkowicie wyłączyć rekreacjiActivity
. Zachowaj stan zgodnie z opisem w sekcji Zapisywanie stanów interfejsu użytkownika. - Nie unikaj zmian konfiguracji: nie narzucaj ograniczeń dotyczących orientacji, proporcji obrazu ani możliwości zmiany rozmiaru, aby uniknąć zmian w konfiguracji i przywracania
Activity
. Ma to negatywny wpływ na użytkowników, którzy chcą korzystać z Twojej aplikacji w wybrany przez siebie sposób.
Obsługa zmian konfiguracji na podstawie rozmiaru
Zmiany konfiguracji na podstawie rozmiaru mogą następować w dowolnym momencie. Są one bardziej prawdopodobne, gdy aplikacja działa na dużym ekranie, na którym użytkownicy mogą włączyć tryb wielu okien. Oczekują, że Twoja aplikacja będzie dobrze działać w danym środowisku.
Wyróżniamy 2 ogólne typy zmian rozmiaru: istotne i nieistotne. Znaczna zmiana rozmiaru polega na zastosowaniu innego zestawu zasobów alternatywnych do nowej konfiguracji z powodu różnicy w rozmiarze ekranu (np. szerokości, wysokości lub najmniejszej szerokości). Zasoby te obejmują elementy zdefiniowane przez aplikację oraz zasoby z jej bibliotek.
Ogranicz odtwarzanie aktywności w celu zmiany konfiguracji na podstawie rozmiaru
Jeśli wyłączysz odtwarzanie Activity
w przypadku zmian konfiguracji na podstawie rozmiaru, system nie utworzy ponownie zasobu Activity
. Zamiast tego otrzyma wywołanie Activity.onConfigurationChanged()
. Wszystkie dołączone widoki będą otrzymywać wywołanie do View.onConfigurationChanged()
.
Odtwarzanie Activity
jest wyłączone w przypadku zmian konfiguracji na podstawie rozmiaru, jeśli plik manifestu zawiera parametr „android:configChanges="screenSize|smallestScreenSize|orientation|screenLayout
”.
Zezwalaj na odtwarzanie aktywności w przypadku zmian konfiguracji na podstawie rozmiaru
W Androidzie 7.0 (poziom interfejsu API 24) i nowszych odtworzenie Activity
odbywa się tylko w przypadku zmian konfiguracji na podstawie rozmiaru, jeśli jest to znacząca. Jeśli system nie odtworzy Activity
z powodu niewystarczającego rozmiaru, może w zamian wywołać metody Activity.onConfigurationChanged()
i View.onConfigurationChanged()
.
Istnieją pewne zastrzeżenia dotyczące wywołań zwrotnych Activity
i View
gdy parametr Activity
nie został odtworzony:
- W Androidzie od 11 (poziom interfejsu API 30) do 13 (poziom interfejsu API 33) usługa
Activity.onConfigurationChanged()
nie jest wywoływana. - Występuje znany problem, który powoduje, że w niektórych przypadkach funkcja
View.onConfigurationChanged()
może nie być wywoływana na Androidzie 12L (poziom interfejsu API 32) i wczesnych wersjach Androida 13 (poziom API 33). Więcej informacji znajdziesz w tym publicznym wydaniu. Ten problem został rozwiązany w późniejszych wersjach Androida 13 i 14.
W przypadku kodu, który wymaga nasłuchiwania zmian konfiguracji zależnie od rozmiaru, zalecamy użycie narzędzia View
z zastąpionym View.onConfigurationChanged()
, zamiast polegać na odtwarzaniu Activity
lub Activity.onConfigurationChanged()
.