Obsługa zmian konfiguracji

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

  • Rozmiar interfejsu aplikacji
  • Orientacja ekranu
  • Rozmiar i grubość czcionki
  • Język
  • Tryb ciemny a tryb jasny
  • Dostępność klawiatury

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

Te parametry zwykle wymagają wprowadzenia tak dużych zmian w interfejsie aplikacji, że platforma Android ma specjalny mechanizm, który obsługuje te zmiany. Ten mechanizm to Activityrekonstrukcja.

Rekreacja

Gdy nastąpi zmiana konfiguracji, system ponownie utworzy Activity. W tym celu system wywołuje funkcję onDestroy() i niszczy istniejący element Activity. Następnie tworzy nową instancję za pomocą onCreate(), a ta nowa instancja Activity jest inicjowana za pomocą nowej, zaktualizowanej konfiguracji. Oznacza to też, że system ponownie tworzy interfejs użytkownika z nową konfiguracją.

Zachowanie podczas odtwarzania pomaga aplikacji dostosować się do nowych konfiguracji poprzez automatyczne ponowne wczytywanie aplikacji z alternatywnymi zasobami pasującymi do nowej konfiguracji urządzenia.

Przykład dotyczący zajęć rekreacyjnych

Rozważ element TextView, który wyświetla tytuł statyczny za pomocą elementu android:text="@string/title" zdefiniowanego w pliku XML układu. Gdy widok zostanie utworzony, tekst zostanie ustawiony dokładnie raz na podstawie bieżącego języka. Jeśli język się zmieni, system ponownie utworzy aktywność. W związku z tym system ponownie tworzy widok i inicjalizuje go z użyciem prawidłowej wartości na podstawie nowego języka.

Podczas jego tworzenia usuwa się też wszystkie stany przechowywane jako pola w Activity lub w zawartych w nim obiektach Fragment, View lub innych obiektach. Dzieje się tak, ponieważ rekreacja Activity tworzy zupełnie nową instancję Activityi interfejs użytkownika. Ponadto stary obiekt Activity nie jest już widoczny ani ważny, więc wszystkie pozostałe odwołania do niego lub jego obiektów są nieaktualne. Mogą one 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 wielookiennym, aby sprawdzić informacje, a potem wraca do usuniętego formularza lub do innego miejsca w aplikacji, może to wywołać u niego negatywne wrażenia. Jako deweloper musisz zapewnić użytkownikom spójne wrażenia poprzez zmiany konfiguracji i powtarzanie aktywności.

Aby sprawdzić, czy stan jest zachowany w aplikacji, możesz wykonywać czynności, które powodują zmiany konfiguracji zarówno wtedy, gdy aplikacja działa na pierwszym planie, jak i w tle. Dotyczy to następujących czynności:

  • Obrócenie urządzenia
  • Włączanie trybu wielu okien
  • zmiana rozmiaru aplikacji w trybie wielu okien lub okna o dowolnym kształcie;
  • Składanie składanego urządzenia z wieloma wyświetlaczami
  • zmiana motywu systemu, np. z ciemnego na jasny;
  • Zmiana rozmiaru czcionki
  • Zmienianie języka systemu lub aplikacji
  • Podłączanie i odłączanie klawiatury sprzętowej
  • Podłączanie i odłączanie stacji dokującej

Aby zachować odpowiedni stan za pomocą funkcji Activity odtwarzania, możesz zastosować 3 podstawowe podejścia. Wybór odpowiedniej metody zależy od typu stanu, który chcesz zachować:

  • Lokalna trwałość do obsługi zatrzymywania procesu w przypadku złożonych lub dużych danych. Stałe miejsce na dane lokalne obejmuje bazy danych lub DataStore.
  • Zachowaj obiekty, takie jak instancje ViewModel, aby zarządzać stanem interfejsu w pamięci podczas aktywnego korzystania z aplikacji.
  • Zapisane stany instancji – służą do obsługi zamykania procesów inicjowanych przez system i zachowywania stanu przejściowego, który zależy od danych wejściowych użytkownika lub nawigacji.

Aby dowiedzieć się więcej o każdym z tych interfejsów API oraz o tym, kiedy ich używać, zapoznaj się z artykułem Zachowywanie stanów interfejsu użytkownika.

Ograniczanie odtwarzania aktywności

Możesz uniemożliwić automatyczne tworzenie aktywności w przypadku niektórych zmian konfiguracji. Odtworzenie Activity powoduje odtworzenie całego interfejsu użytkownika oraz wszystkich obiektów pochodnych z Activity. Możesz mieć ku temu dobre powody. Na przykład aplikacja może nie potrzebować aktualizacji zasobów podczas określonej zmiany konfiguracji lub może mieć ograniczenie wydajności. W takim przypadku możesz zadeklarować, że Twoja aktywność sama obsługuje zmianę konfiguracji i zapobiega ponownemu uruchamianiu aktywności przez system.

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

Gdy orientacja ekranu i dostępność klawiatury ulegną zmianie, ten kod pliku manifestu wyłącza Activity dla MyActivity:

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

Niektóre zmiany konfiguracji zawsze powodują ponowne uruchomienie aktywności. Nie możesz 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 View;

W systemie View, gdy nastąpi zmiana konfiguracji, dla której masz wyłączoną rekreację Activity, aktywność otrzyma wywołanie Activity.onConfigurationChanged(). Wszystkie załączone widoki otrzymają również prośbę o udzielenie informacji na temat View.onConfigurationChanged(). W przypadku zmian konfiguracji, których nie dodano do pliku android:configChanges, system ponownie tworzy aktywność w zwykły sposób.

Metoda wywołania zwrotnego onConfigurationChanged() otrzymuje obiekt Configuration, który określa nową konfigurację urządzenia. Aby dowiedzieć się, jak wygląda Twoja nowa konfiguracja, przeczytaj pola obiektu Configuration. Aby wprowadzić kolejne zmiany, zaktualizuj zasoby, których używasz w interfejsie. Gdy system wywołuje tę metodę, obiekt Resources aktywności jest aktualizowany, aby zwracać zasoby na podstawie nowej konfiguracji. Dzięki temu możesz zresetować elementy interfejsu bez restartowania aktywności.

Na przykład implementacja onConfigurationChanged() sprawdza, czy klawiatura jest dostępna:

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 ze względu na te zmiany konfiguracji, możesz zamiast tego nie stosować się do onConfigurationChanged(). W takim przypadku nadal używane są wszystkie zasoby używane przed zmianą konfiguracji. Zapobiega to tylko ponownemu uruchamianiu aktywności. Na przykład aplikacja na telewizor może nie reagować na podłączenie lub odłączenie klawiatury Bluetooth.

Zachowaj stan

Stosując tę technikę, musisz zachować stan podczas normalnego cyklu aktywności. Dzieje się tak, ponieważ:

  • Nieuniknione zmiany: zmiany konfiguracji, których nie można zapobiec, mogą spowodować ponowne uruchomienie aplikacji.
  • Śmierć procesu: aplikacja musi być w stanie obsłużyć śmierć procesu zainicjowaną przez system. Jeśli użytkownik opuści aplikację, a ona przejdzie na drugi plan, system może ją zamknąć.

Reagowanie na zmiany konfiguracji w Jetpack Compose

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

Obiekt Configuration jest dostępny w hierarchii interfejsu tworzenia wiadomości z kompozycją lokalną LocalConfiguration. Gdy nastąpi zmiana, funkcje kompozytowe odczytujące z LocalConfiguration.current zostaną ponownie skompilowane. Informacje o tym, jak działają dane lokalne, znajdziesz w artykule Dane ograniczone lokalnie za pomocą funkcji CompositionLocal.

Przykład

W tym przykładzie komponent wyświetla datę w określonym formacie. Składnik reaguje na zmiany konfiguracji języka systemu, wywołując komponent 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ąć tworzenia Activity przy zmianie lokalizacji, Activity hostujący kod Compose musi zrezygnować ze zmian konfiguracji lokalizacji. Aby to zrobić, ustaw wartość android:configChanges na locale|layoutDirection.

.

Zmiany konfiguracji: kluczowe pojęcia i sprawdzone metody

Oto najważniejsze pojęcia, które musisz znać, gdy wprowadzasz zmiany w konfiguracji:

  • Konfiguracje: konfiguracje urządzenia określają sposób wyświetlania interfejsu użytkownikowi, np. rozmiar aplikacji, lokalizację lub motyw systemu.
  • Zmiany konfiguracji: konfiguracje zmieniają się w reakcji na działania użytkownika. Użytkownik może na przykład zmienić ustawienia urządzenia lub sposób fizycznego korzystania z urządzenia. Nie ma możliwości zapobiegania zmianom konfiguracji.
  • Activity odtworzenie: zmiany w konfiguracji powodują domyślnie Activity odtworzenie. Jest to wbudowany mechanizm ponownego inicjowania stanu aplikacji w przypadku nowej konfiguracji.
  • Activity zniszczenie: utworzenie nowej instancji Activity powoduje zniszczenie starej instancji Activity i utworzenie nowej na jej miejscu. Stara instancja jest teraz nieaktualna. Wszelkie pozostałe odwołania do niego powodują wycieki pamięci, błędy lub awarie.
  • Stan: stan w starym wystąpieniu Activity nie jest obecny w nowym wystąpieniu Activity, ponieważ są to 2 różne instancje obiektu. Zachowaj stan aplikacji i użytkownika zgodnie z opisem w artykule Zapisywanie stanów interfejsu użytkownika.
  • Wyłączenie: wyłączenie odtwarzania aktywności w przypadku danego typu zmiany konfiguracji może być potencjalną optymalizacją. Wymaga to odpowiedniej aktualizacji aplikacji w odpowiedzi na nową konfigurację.

Aby zapewnić użytkownikom wygodę, postępuj zgodnie z tymi sprawdzonymi metodami:

  • Bądź przygotowany na częste zmiany konfiguracji: nie zakładaj, że zmiany konfiguracji są rzadkie lub w ogóle nie występują, niezależnie od poziomu interfejsu API, formy lub zestawu narzędzi interfejsu użytkownika. Gdy użytkownik wprowadza zmiany w konfiguracji, oczekuje, że aplikacje zostaną zaktualizowane i nadal będą działać prawidłowo z nową konfiguracją.
  • Zachowaj stan: nie trać stanu użytkownika podczas Activity odtwarzania. Zachowaj stan zgodnie z opisem w artykule Zapisywanie stanów interfejsu użytkownika.
  • Unikaj rezygnacji jako szybkiego rozwiązania: nie rezygnuj z reprodukcji Activity jako skrótu do unikania utraty stanu. Wyłączenie odtwarzania aktywności wymaga spełnienia obietnicy dotyczącej obsługi zmiany. Nadal możesz utracić stan z powodu odtworzenia Activity z powodu innych zmian konfiguracji, zakończenia procesu lub zamknięcia aplikacji. Nie można całkowicie wyłączyć odtwarzania Activity. Zachowaj stan zgodnie z opisem w artykule Zapisywanie stanów interfejsu użytkownika.
  • Nie unikaj zmian konfiguracji: nie nakładaj ograniczeń na orientację, współczynnik proporcji ani możliwość zmiany rozmiaru, aby uniknąć zmian konfiguracji i Activity. Ma to negatywny wpływ na użytkowników, którzy chcą korzystać z aplikacji w wybrany przez siebie sposób.

Obsługa zmian konfiguracji związanych z rozmiarem

Zmiany konfiguracji związane z rozmiarem 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 wielookienności. Oczekują, że aplikacja będzie dobrze działać w takim środowisku.

Istnieją 2 ogólne typy zmian rozmiaru: znaczące i nieznaczne. Znaczne zmiany rozmiaru to takie, w których przypadku do nowej konfiguracji stosuje się inny zestaw zasobów alternatywnych z powodu różnicy w rozmiarze ekranu, np. szerokości, wysokości lub najmniejszej szerokości. Te zasoby obejmują zasoby zdefiniowane przez aplikację i zasoby z dowolnej jej biblioteki.

Ograniczenie ponownego tworzenia aktywności w przypadku zmian konfiguracji związanych z rozmiarem

Gdy wyłączysz odtwarzanie Activity w przypadku zmian konfiguracji związanych z rozmiarem, system nie odtworzy Activity. Zamiast tego otrzyma połączenie do Activity.onConfigurationChanged(). Wszystkie widoki z dołączonymi widokami otrzymują połączenie do View.onConfigurationChanged().

Tworzenie Activity jest wyłączone w przypadku zmian konfiguracji opartych na rozmiarze, jeśli w pliku manifestu masz opcję android:configChanges="screenSize|smallestScreenSize|orientation|screenLayout".

Zezwalanie na odtwarzanie aktywności w przypadku zmian konfiguracji związanych z rozmiarem

W Androidzie 7.0 (poziom interfejsu API 24) i nowszych wersjach Activityrekonstrukcja tylko występuje w przypadku zmian konfiguracji opartych na rozmiarze, jeśli zmiana rozmiaru jest znacząca. Gdy system nie może odtworzyć Activity z powodu niewystarczającego rozmiaru, może wywołać Activity.onConfigurationChanged()View.onConfigurationChanged().

W przypadku funkcji zwrotnych ActivityView należy pamiętać o kilku ograniczeniach, gdy element Activity nie jest ponownie tworzony:

  • W Androidzie w wersji od 11 (poziom API 30) do 13 (poziom API 33) funkcja Activity.onConfigurationChanged() nie jest wywoływana.
  • Istnieje znany problem, który w niektórych przypadkach może powodować, że funkcja View.onConfigurationChanged() nie będzie wywoływana na Androidzie 12L (poziom API 32) i w pierwszych wersjach Androida 13 (poziom API 33). Więcej informacji znajdziesz w tym publicznym zgłoszeniu. Ten problem został rozwiązany w późniejszych wersjach Androida 13 i Androida 14.

W przypadku kodu, który zależy od zmian konfiguracji opartych na rozmiarze, zalecamy użycie narzędzia View z zastąpionym parametrem View.onConfigurationChanged() zamiast polegania na funkcji Activity lub Activity.onConfigurationChanged().