środa, lipca 14, 2010

Modale nie takie dobre dla Androida ;-)

W niedzielę pisałem o oknach modalnych i zaprezentowałem jak można w Androidzie zrobić sobie okno modalne, które blokuje przepływ, a więc upraszcza też logikę aplikacji.........Niestety rozwiązanie, które sprzedałem ma pewien problem.W Androidzie konfiguracja sprzętu może zmieniać się w locie, głównie chodzi o orientację ekranu [choć nie zdziwiłbym się jakby za jakiś czas doszła też zmiana ekranu ;-) gdy np. pojawią się tablety ze złączem HDMI].Taka zmiana powoduje zaś odrysowanie UI aplikacji. Oznacza to tyle, że nasza stara aktywność jest niszczona, a nowa jest uruchamiana i wołane są jej metody onCreate() a następnie onResume().Niestety to odrysowywanie UI ma też konsekwencję dla okien dialogowych. Znikają.Moja metoda polegała na tym, że tworzyłem okno dialogowe, które z wątkiem "głównym" komunikowało się przez pewien obiekt, a żeby wątek "główny" głupio procesora nie żarł to czekał na monitorze związanym z obiektem do przekazywania rezultatów. Naciśnięcie któregoś z klawiszy dialoga powodowało zaś ustawienie rezultatu, a następnie zawołanie notifyAll() na tym obiekcie dzięki czemu wątek "główny" był budzony i sterowanie wracało do naszego zatrzymanego kodu.Rozwiązanie genialne ;) ale ma wadę.Ponieważ dialog znika po zmianie orientacji ekranu to zostajemy z ekstra wątkiem [tym "głównym", który czeka na wynik dialoga], który wisi i czeka na obiekcie rezultatu. Nie zostanie on jednak nigdy celowo obudzony przez nasz kod, a nawet jeśli się to jakoś zdarzy to po sprawdzeniu, że użytkownik nie kliknął na żaden z guziczków dialoga wróci do czekania na obiekcie rezultatu.Dodatkowo w pamięci będą siedziały też wszystkie obiekty używane przez nasz wątek, czyli aktywność dla której stworzono i pokazano dialog [ta aktywność, która została "zamknięta" po zmianie orientacji] i wszystko co zostało już jakoś w tym wątku użyte...... a ponieważ wątek to zwykle wewnętrzna klasa anonimowa i to taka złośliwa, że nie jest statyczna (dla przypomnienia, wewnętrzna klasa musi mieć dodatkowe mechanizmy do trzymania kontaktów z klasą zewnętrzną, dlatego gdy chcemy mieć klasę wewnętrzną, a nie wymaga ona związku z klasą zewnętrzną to używamy modyfikatora static do zerwania tego łącza co wpływa na performance ;-)) więc trzymamy w pamięci de facto wszystko związane ze starą aktywnością......... A to nie jest fajne.Dlatego mimo wszystko odradzam używanie mojego confirm() :-)Co prawda nie jest do końca powiedziane, że nie da się tego obejść ;-) wystarczyłoby sprawić by wątek "główny", który czeka na rezultat nie robił wait() a robił wait(int), czyli czekał przez zadany czas, po czym sprawdzałby czy jego aktywność w ogóle działa i jeżeli by działała wracałby do czekania, a w przeciwnym przypadku wywalałby wyjątek..... nie jest to ładne, ale dalej pozwoliłoby się cieszyć w miarę wygodnym confirm().Ale jak na razie moje próby ugryzienia problemu spełzły na niczym bo nie potrafię wykryć tego stanu gdy aktywność została zgubiona........Z dodatkowych bonusów, także ProgressDialog na to choruje, tzn. znika po zmianie orientacji ekranu :-( Jeżeli kontroluje go jakiś zewnętrzny wątek to wątek ten nadal sobie biegnie, ale już jego aktywność nie żyje, a dokładniej nie żyje tak jak powinna ;-) dlatego zawołanie na niej np. finish() spowoduje wywalenie wyjątku......Android trudny.......


Podobne postybeta
Modale dobre - confirm dla Androida :-)
Prototypowanie dla Androida
wait() i notify()/notifyAll() - najbardziej nierozumiane metody klasy Object ;-)
O wyższości klas anonimowych
O tym w czym iOS jest lepszy od Androida