Sunday, September 19, 2010

Kapryśny Android

Sprawa dotyczy zachowania się activity podczas obrotu. Aby obrócić ekran emulatora należy użyć CTRL+F12. Wspomniana kombinacja klawiszy obraca ekran o 90 stopni. Możemy w ten sposób przetestować jak zachowa się nasza aplikacja podczas obracania rzeczywistego urządzenia. Co się okazuje (ja byłem całkiem mocno zaskoczony :)) podczas takiego obrotu niszczone jest bieżące activity, a następnie tworzone nowe. Niezła niespodzianka, prawda? ;)

Problem okazał się całkiem poważny, gdyż podczas onCreate oraz onDestroy (a dokładniej podczas tworzenia / niszczenia powierzchni w KasuroidView), kasuroid jest odpowiednio inicjalizowany / kończy pracę. Okazało się, że startowanie Corea w KasuroidView nie jest dobrym pomysłem (szczerze mówiąc od samego początku takie rozwiązanie mi się nie podobało i prędzej czy później chciałem to zmienić).

Pierwszym podejściem do rozwiązania problemu było zablokowanie przejścia do trybu landscape (obrót o 90 stopni w stosunku do trybu portrait, w którym odpalany jest emulator). Aby wymusić dany tryb wyświetlania aplikacji (bez możliwości jego zmiany) należy w manifeście dla odpowiedniego activity dodać android:screenOrientation="portrait". Niestety (jak można się domyślić) nie rozwiązało to problemu w 100% :) Ekran owszem nie jest skalowany do trybu landscape przy obrocie, ale activity nadal jest niszczone. Na dodatek chciałbym, żeby kasuroid jednak wspierał tryb landscape.

Chwila szukania / czytania API i.. jest światełko w tunelu. Okazuje się, że na activity tuż przed onDestroy wołana jest metoda onSaveInstanceState. Umożliwia ona zapisanie stanu aplikacji, który następnie może zostać odtworzony w onCreate

Na teraz nie bardzo sobie wyobrażam jakie dane mógłbym zapisywać (w przypadku gry mogą to być jednak parametry leveli itd.), ale to co mogę zrobić w takiej sytuacji to zabezpieczyć Core przed restartowaniem. Warto wspomnieć, że Core jest singletonem. Co za tym idzie obiekt Corea przechowywany jest w zmiennej statycznej, a te nie są niszczone, jeśli activity jest przenoszone do hmm.. tła (minimializowanie) lub np. jak w tym przypadku, następuje obrót ekranu.

Przede wszystkim rozszerzyłem interfejs Corea o możliwość zatrzymywania i włączania głównego wątku (metody stop() oraz start()). Wątek nie robi nic poza wołaniem runSlice, więc można spokojnie nim manipulować ;). Następnie cały proces inicjalizacji / przerywania pracy / startowania Corea przeniosłem do KasuroidActivity.

A w jaki sposób sprawdzić czy activity jest jedynie maksymalizowane czy też tworzone całkiem od nowa? Hmm.. zastosowałem trochę "hardkorowe" podejście i pewnie zostanę mocno skarcony przez znawców Androida :P. Zauważyłem, że jeśli activity wraca do pierwszego planu to parametr w onCreate nie jest zerowy (lub nie jest nullem). Wystarczy zatem sprawdzić uchwyt do Bundle przekazanego jako parametr i wtedy albo uaktualniamy jedynie powierzchnię Renderera albo inicjalizujemy Corea. Następnie wystarczy już tylko wystartować wątek Corea.

W związku z powyższą zmianą logiki startu activity, do KasuroidActivity dodałem dwie metody onInit oraz onTerm. onInit wołana jest w przypadku kiedy tworzone jest nowe activity, natomiast onTerm, kiedy activity permanentnie jest niszczone.

W przypadku wspomnianego w poprzednim wpisie testu stanów gry, inicjalizacja pierwszego stanu została przeniesiona z onCreate do onInit (rozwiązuje to problem zmiany stanu, w przypadku gdy activity przeszło do tła przy aktywnym stanie drugim).

Źródła uaktualnione, więc zachęcam do podejrzenia kodu.

Btw. cykl życia androidowego activity jest dość ciekawy i bardzo możliwe, że w trakcie rozwijania projektu KasuroidActivity trochę się pozmienia :) 

Jeszcze takie spostrzeżenie. Praca nad kasuroid w całkiem dużym stopniu przypomina programowanie ekstremalne ;)

Do następnego!

No comments:

Post a Comment