Cześć, w ubiegłym tygodniu pisałem na temat cyklu życia aktywności. Nawiązując do tego tematu, chciałbym poruszyć kwestię obrotu urządzenia np. z układu pionowego na poziomy. W androidowych aplikacjach bardzo często mamy do czynienia z problemami po obróceniu urządzenia. Problemy te często nie są wykrywane w czasie testowania aplikacji na emulatorze. Związane są one z ponownym wywołaniem metody onCreate() w momencie, gdy Android wykryje zmianę orientacji urządzenia. Istnieją dwa sposoby na rozwiązanie tego problemu. Postaram się je przybliżyć poniżej.
Zacznijmy od początku. Co jest przyczyną błędu, w momencie obrotu urządzenia?
Przedstawmy ten problem na przykładzie aplikacji stopera. Pierwszym krokiem jest włączenie przez użytkownika aplikacji i rozpoczęcie działania stopera. Metoda runTimer() rozpoczyna inkrementację liczby sekund wyświetlanych poprzez widok tekstowy o identyfikatorze time_view, który wykorzystuje do tego celu dwie zmienne: seconds i running. W tym momencie dochodzi do obrotu urządzenia przez użytkownika. Android zauważa, iż nastąpiła zmiana orientacji urządzenia i wielkości ekranu – co prowadzi do usunięcia działającej aktywności, włącznie z używanymi zmiennymi, używanymi przez metodę runTimer(). Kolejnym etapem jest ponowne utworzenie aktywności StopwatchActivity. Metoda onCreate() aktywności zostaje wykonana ponownie, czego następstwem jest wywołanie metody runTimer(). Ponowne utworzenie aktywności skutkuje przyjęciem przez zmienne seconds i running wartości domyślnych.
W momencie uruchomienia aplikacji i rozpoczęcia wykonywania aktywności przez Androida, uwzględniana jest konfiguracja urządzenia. Obejmuje ona zarówno konfigurację fizycznego urządzenia (tj. wielkość ekranu, jego orientacja, dostępność podłączonej klawiatury), jak i opcje konfiguracyjne, określane przez użytkownika (np. wybrane ustawienia lokalne).
Znajomość konfiguracji urządzenia przez Androida jest konieczna w chwili uruchamiania aktywności, ponieważ może mieć ona realny wpływ na zasoby niezbędne dla działania aplikacji. Przykładowo – inny układ może być wykorzystywany, gdy urządzenie znajduje się w orientacji pionowej, a inny – w orientacji poziomej. W momencie zmiany konfiguracji urządzenia, konieczna jest aktualizacja wszystkiego, co prezentuje interfejs użytkownika – w taki sposób, by odpowiadał on nowej konfiguracji.
Jak temu zaradzić?
Przejdźmy teraz do sedna sprawy. W jaki sposób możemy poradzić sobie ze zmianami konfiguracji? Jak wspomniałem we wstępie, istnieją dwa rozwiązania naszego problemu.
Rozwiązanie nr 1 – pomijamy ponowne utworzenie aktywności
Nakazujemy systemowi, aby nie uruchamiał ponownie aktywności po wykryciu zmiany konfiguracji urządzenia. Należy jednak pamiętać, iż często nie jest to najlepszym rozwiązaniem – wynika to z faktu, iż gdy Android odtwarza aktywność, jednocześnie upewnia się, czy będzie ona wykorzystywać zasoby odpowiadające bieżącej konfiguracji. Pominięcie tego systemowego mechanizmu może więc prowadzić do napisania przez nas dodatkowego kodu, niezbędnego do obsługi nowej konfiguracji.
Aby zablokować przez Androida odtwarzanie aktywności po zmianie konfiguracji urządzenia, należy dodać do elementu activity w pliku manifestu AndroidManifest.xml następujący atrybut:
android:configChanges=”zmiana_konfiguracji”
gdzie zmiana_konfiguracji jest typem zmiany konfiguracji urządzenia.
Nas będzie interesowało pominięcie zmiany wielkości i orientacji ekranu, dlatego też musimy dodać do pliku AndroidManifest.xml poniższy fragment kodu:
<activity
android:name=”com.hfad.stoper.StopwatchActivity”
android.label=”@string/app_name”
android:configChanges=”orientation|screenSize” >
gdzie pionowa kreska “|” oznacza, iż należy pomijać obie podane zmiany konfiguracji.
Jeżeli Android rozpozna zmianę konfiguracji, której typ został przez nas podany w atrybucie, to zamiast standardowego odtwarzania aktywności, wywołana zostanie metoda onConfigurationChanged(Configuration):
public void onConfigurationChanged(Configuration config) {
}
Powyższa metoda może być stosowana do obsługiwania zmian w konfiguracji, o ile pojawi się taka konieczność.
Rozwiązanie nr 2 – zapisujemy bieżący stan i odtwarzamy go ponownie w onCreate()
Rozwiązanie to jest lepszym sposobem obsługi zmian konfiguracji. Polega na konieczności zaimplementowania metody onSaveInstanceState(). Jest ona wykonywana przed usunięciem aktywności i zapisuje jej bieżący stan. Metoda ta posiada jeden parametr – obiekt klasy Bundle, który pozwala na gromadzenie danych różnych typów w jednym obiekcie.
public void onSaveInstanceState(Bundle savedInstanceState) {
}
Obiekt klasy Bundle jest również przekazywany do metody onCreate(), a metoda ta ma następująca postać:
bundle.put*(“nazwa”, wartość)
Dane z obiektu Bundle możemy pobrać za pomocą metody:
bundle.get*(“nazwa”);
Dzięki poznanym rozwiązaniom jesteśmy w stanie zabezpieczyć się przed niechcianą utratą danych w momencie zmiany konfiguracji urządzenia np. obrotu urządzenia z pozycji pionowej na poziomą.
Na dzisiaj to już wszystko. Życzę wszystkim miłego weekendu. 