Cykl życia aplikacji  

Udostępnij na: Facebook

Autor: Marcin Kruszyński

Opublikowano: 2010-12-10

Aby budować w prawidłowy sposób aplikacje na platformę Windows Phone 7, koniecznie należy poznać i zrozumieć ich model wykonywania. Został on zaprojektowany w taki sposób, by zapewnić użytkownikom telefonu maksymalną responsywność mimo ograniczonych zasobów. Wiąże się to z założeniem, że w określonym czasie może wykonywać się tylko jedna aplikacja, będąca na pierwszym planie, z którą użytkownik jest w interakcji. Żadna z aplikacji firm trzecich nie może działać w tle. Zadaniem programisty jest sprawienie, by użytkownik miał wrażenie ciągłego wykonywania się aplikacji.     

Po przeczytaniu tego artykułu będziesz:

  • znał precyzyjnie przebieg cyklu życia aplikacji,
  • wiedział, w jaki sposób obsługiwać związane z nim sytuacje.

Wprowadzenie

Aplikacje możemy uruchamiać z listy zainstalowanych aplikacji lub za pomocą skrótu na ekranie startowym.  Utrzymywany jest dziennik z wcześniej odwiedzonymi aplikacjami i stronami. Za pomocą sprzętowego klawisza Back możemy nawigować między nimi wstecz.

Przy nawigacji pomiędzy aplikacjami system dynamicznie dokonuje ich aktywacji i dezaktywacji. Ze zmianami stanu aplikacji związane są odpowiednie zdarzenia. W ich obsłudze możemy dokonać zapamiętania i odtwarzania informacji o stanie, dzięki czemu może to sprawiać wrażenie, że aplikacja wykonywała się cały czas w tle.

W analizie cyklu życia będziemy często posługiwali się następującymi pojęciami:

  • Tombstoning – procedura, w której system kończy proces aplikacji, aby mogła zostać uruchomiona inna aplikacja lub funkcjonalność (np. odebranie przychodzącej rozmowy) i te informacje o stanie aplikacji są przechowywane. Kiedy nastąpi ponowne do niej przenawigowanie, system uruchamia na nowo jej proces i przekazuje do niego wcześniej zapamiętane dane.
  • Stan strony – wizualny stan danej strony, np. pozycja paska do przewijania, zawartość pola tekstowego itp.
  • Stan aplikacji – stan aplikacji niezwiązany z konkretną stroną.
  • Dane trwałe – dane współdzielone przez wszystkie instancje aplikacji, np. ustawienia, zapisywane i ładowane z Isolated Storage (przestrzeń do trzymania plików przez daną aplikację – więcej na ten temat dowiesz się z artykułu Isolated Storage).
  • Dane tymczasowe – dane opisujące stan pojedynczej instancji aplikacji, np. wynik wywołania usługi web service, mogą być odtwarzane przy ponownej aktywacji aplikacji objętej procedurą tombstoning.

Cykl życia aplikacji

Klasa PhoneApplicationService posiada cztery zdarzenia: Launching, Activated, Deactivated, Closing, które wiążą się z odpowiednimi stanami cyklu życia aplikacji (rys. 1):

  • Launching – przy uruchamianiu aplikacji w każdy inny sposób, niż przez powrót do niej klawiszem Back. Aplikacja wchodzi w ten stan, kiedy  naciskamy na pozycję w liście zainstalowanych aplikacji lub kafelek na ekranie startowym. Następuje to także w innych sytuacjach, np. przy naciśnięciu na komunikat notyfikacji toast. Tworzona jest wówczas nowa instancja aplikacji. 

  • Running – po uruchomieniu aplikacja się wykonuje.

  • Closing – gdy za pomocą klawisza Back nawigujemy z aplikacji, przechodząc przez pierwszą jej stronę. Proces aplikacji zostaje zakończony.


    Rys.1. Przebieg cyklu życiowego aplikacji.

  • Deactivating – gdy aplikacja działająca na pierwszym planie zostanie zastąpiona przez inną aplikację. Następuje to przy naciśnięciu przycisku Start i przy zablokowaniu ekranu (co można wyłączyć). Dezaktywację aplikacji może spowodować również wywołanie z jej poziomu launchera lub choosera (pomocnicze aplikacje do korzystania z funkcjonalności telefonu – więcej przeczytasz o nich w artykułach Launchers i Choosers). Z dezaktywacją bardzo często może się wiązać tombstoning. Nie ma jednak gwarancji, że aplikacja poddana tombstoningowi będzie reaktywowana, użytkownik może do niej już nie powrócić. Możemy natomiast zapisać dane, które okażą się przydatne przy reaktywacji. Dezaktywacja może zachodzić bez tombstoningu, gdy szybko naciśniemy kolejno po sobie klawisze Start i Back oraz przy wywołaniu niektórych launcherów i chooserów. Proces zostanie wtedy tylko uśpiony, a nie zakończony.

  • Activating – zdezaktywowana aplikacja poddana tombstoningowi może zostać reaktywowana. Następuje to w sytuacji, gdy użytkownik, naciskając przycisk Back, natrafia na daną aplikację lub po powrocie z wywołania launchera lub choosera. Przy reaktywacji system tworzy nową instancję aplikacji, do której możemy przekazać zapisane podczas dezaktywacji dane. Automatycznie wyświetlana jest ostatnia strona, którą użytkownik wcześniej odwiedził.

Zdarzenia Launching i Activated oraz Closing i Deactivated wykluczają się parami.

Wśród launcherów i chooserów można wyróżnić takie, które przy wystarczającej ilości zasobów systemowych nie powodują tombstoningu. Są to: PhotoChooserTask, CameraCaptureTask, MediaPlayerLauncher, EmailAddressChooserTask, PhoneNumberChooserTask, Multiplayer Game Invite (XNA), Gamer You Card (XNA). Zostało to podyktowane chęcią zapewnienia lepszej płynności w działaniu interfejsu użytkownika.

Zapisywanie i odtwarzanie stanu aplikacji

Szablony projektów na Windows Phone 7 dostarczane przez standardowe narzędzia w pliku App.xaml.cs zawierają predefiniowane handlery do zdarzeń cyklu życia aplikacji.

Obsługując zdarzenie Launching nie powinniśmy próbować odczytywać tymczasowych danych z poprzedniej instancji, aplikacja powinna zawsze wyglądać jak nowa instancja. Zdarzenie zachodzi zanim aplikacja staje się widoczna i aktywna, nie jest więc wskazany tutaj odczyt danych z Isolated Storage, będący dość czasochłonną operacją. Lepiej zrobić to asynchronicznie po załadowaniu aplikacji.

Podczas działania aplikacji możemy opcjonalnie stopniowo zapisywać trwałe dane do Isolated Storage, aby zmniejszyć liczbę zapisywanych danych w momencie zmiany stanu aplikacji.

W obsłudze zdarzenia Closing powinniśmy zapisać trwałe dane do Isolated Storage. Nie ma potrzeby zapisywania danych tymczasowych, ponieważ ponowne uruchomienie aplikacji może dokonać się tylko przez przejście do stanu launching.

private void Application_Closing(object sender, ClosingEventArgs e)
{
      ...

      SaveDataToIsolatedStorage("myDataFile.txt", data);     
}

Dokonując obsługi zdarzenia Deactivated, powinniśmy zapisać dane tymczasowe do słownika w propercji State w klasie PhoneApplicationService. Ponieważ nie ma gwarancji, że aplikacja poddana tombstoningowi zostanie reaktywowana, dodatkowo powinniśmy zapisać trwałe dane do Isolated Storage, tak jak przy zdarzeniu Closing. Wszystkie czynności powinny zajmować maksymalnie 10 sekund. Po tym czasie system zakończy aplikację. Dlatego jeśli mamy dużo danych do zapisania w Isolated Storage, należy czynić to stopniowo w czasie wykonywania się aplikacji. Ze względu na to, że nie przy każdej dezaktywacji występuje tombstoning, ważne jest, aby w obsłudze zdarzenia dezaktywacji nie niszczyć żadnych danych. Aplikacja powinna być w stanie kontynuować działanie bez wywołania handlera zdarzenia Activated.

private void Application_Deactivated(object sender, DeactivatedEventArgs e)
{
      ...          

      PhoneApplicationService.Current.State["UnsavedData"] = unsavedData;

      SaveDataToIsolatedStorage("myDataFile.txt", data);     
}

W obsłudze zdarzenia Activated powinniśmy odtworzyć stan aplikacji na podstawie propercji State z klasy PhoneApplicationService. Podobnie jak w zdarzeniu Launching, nie jest wskazane wykonywanie tutaj długotrwałych operacji, tj. odczytywanie danych z  Isolated Storage.

private void Application_Activated(object sender, ActivatedEventArgs e)
{
      if (PhoneApplicationService.Current.State.ContainsKey("UnsavedData"))

      {               

          var unsavedData = PhoneApplicationService.Current.State["UnsavedData"];

          ...

      }           
}

Implementację metody zapisującej dane do Isolated Storage oraz więcej informacji na temat zapisywania i odtwarzania stanu aplikacji znajdziesz w dokumentacji na stronie How to: Preserve and Restore Application State for Windows Phone.

Zapisywanie i odtwarzanie stanu strony

W niektórych przypadkach bardziej odpowiednie niż na poziomie całej aplikacji jest przechowywanie danych na poziomie danej strony. Dotyczy to sytuacji związanych z UI, gdzie nie wszystkie informacje są bindowane do obiektów z danymi lub nie korzystamy z takiej synchronizacji.

Stan z tymczasowymi danymi strony możemy odczytywać i zapisywać w słowniku w propercji State poprzez nadpisanie metod OnNavigatedTo i OnNavigatedFrom. Należy pamiętać, żeby przy nawigowaniu do strony po raz pierwszy nie próbować odtwarzać jej stanu. Aby wykryć, kiedy strona potrzebuje odtwarzania stanu, możemy zastosować zmienną logiczną oraz zapamiętać fakt zapisania stanu w dodatkowej zmiennej słownika.  Zmienna logiczna może wyglądać następująco:

bool newPageInstance = false;

public MyPage()
{

      InitializeComponent();

      newPageInstance = true;
}

Jej wartość ustawiamy na true w konstruktorze strony, który  wywoływany jest tylko przy pierwszym otwarciu strony lub przy reaktywacji. Strona jest już w pamięci przy przenawigowaniu do niej wstecz z innej strony aplikacji. W metodzie OnNavigatedFrom zapisujemy informację o stanie kontrolek na stronie (pomocnicze metody dobrą praktyką), ustawiamy wartość wcześniej wspomnianej zmiennej na false oraz odnotowujemy sam fakt zapisania stanu.

protected override void OnNavigatedFrom(NavigationEventArgs e)
{
      base.OnNavigatedFrom(e);

      StateUtils.PreserveState(State, txtTitle);

      StateUtils.PreserveFocusState(State, ContentGrid);

      newPageInstance = false;

      this.State["PreservingPageState"] = true;           
}

W metodzie OnNavigatedTo odtwarzamy stan kontrolek na podstawie wcześniej zapisanych informacji, pod warunkiem że zmienna wskazująca na nową instancję strony ma wartość true, a w stanie strony jest ustawiona flaga wskazująca na wcześniejsze jego zachowanie.

protected override void OnNavigatedTo(NavigationEventArgs e)
{
      base.OnNavigatedTo(e);           

      if (newPageInstance && this.State.ContainsKey("PreservingPageState"))

      {

         StateUtils.RestoreState(State, txtTitle, "");              

         StateUtils.RestoreFocusState(State, ContentGrid);

      }          
}

Implementację metod pomocniczych oraz więcej informacji  na temat poruszanego zagadnienia znajdziesz w dokumentacji na stronie How to: Preserve and Restore Page State for Windows Phone.

Wykrywanie bezczynności

Jeśli  aplikacja będzie bezczynna, zostanie zdezaktywowana. Aplikacja traktowana jest jako bezczynna, jeśli ekran został zablokowany. Można to zmienić, ustawiając propercję ApplicationIdleDetectionMode z klasy PhoneApplicationService na wartość Disabled. Nie wyklucza to dezaktywacji aplikacji przez system również z innych powodów, np. wyczerpującej się baterii. W klasie PhoneApplicationFrame mamy zdarzenia Obscured i Unobscured występujące odpowiednio przy zablokowaniu i odblokowaniu ekranu. Powinniśmy je wykorzystać, aby zminimalizować zużycie zasobów przez aplikację przy zablokowanym ekranie.

Gdy użytkownik przestaje dotykać ekran i naciskać przyciski, system po pewnym czasie zablokuje ekran. Ustawienie propercji UserIdleDetectionMode na wartość Disabled spowoduje wyłączenie tej funkcjonalności, choć aplikacja działa dalej. Może to być czasami przydatne np. w grach wykorzystujących akcelerometr. Zalecana jest wtedy własna implementacja wykrywania bezczynności, aby nie wyczerpywać zbytnio baterii.

Więcej informacji na temat wykrywania bezczynności znajdziesz w dokumentacji na stronie Idle Detection for Windows Phone

Podsumowanie

Po przeczytaniu tego artykułu znamy przebieg cyklu życia aplikacji. Rozumiemy, na czym polega tombstoning. Znamy sytuacje, w których następuje dezaktywacja aplikacji. Wiemy również, jak obsługiwać odpowiednie zdarzenia na poziomie aplikacji i pojedynczej strony.


          

Marcin Kruszyński

Absolwent wydziału EAIE Akademii Górniczo-Hutniczej w Krakowie na kierunku Informatyka. Technologiami Microsoft zajmuje się od sześciu lat. Pracuje w firmie ComArch, gdzie tworzy rozwiązania oparte na platformie .NET i Silverlight. Od dwóch lat jest architektem w centrum badawczo-rozwojowym. Wcześniej zajmował się utrzymaniem oraz rozwojem aplikacji webowych w systemie EDI dla dużych sieci handlowych. Interesuje się wieloma aspektami platformy .NET od pierwszej wersji. Obecnie specjalizuje się w implementacji bogatych interfejsów graficznych aplikacji w oparciu o Silverlight (desktop i mobilne) i WPF, których rozwój śledzi od początku ich istnienia. Sporo uwagi poświęca też platformie WCF RIA Services. Jest pasjonatem nowych trendów i technologii (zwłaszcza w wydaniach CTP i beta), których poznawaniem zajmuje się hobbystycznie i zawodowo. Prelegent grup społecznościowych. Prowadził warsztaty z Silverlight w Krakowie (KGD.NET) i Wrocławiu (PGS). Prezentował tworzenie aplikacji w Silverlight na Windows Phone 7 na Visual Studio 2010 Community Launch we Wrocławiu i Krakowie. Pełnił funkcję eksperta w strefie Ask The Expert (ATE) technologii Windows Phone na konferencji Microsoft Technology Summit (MTS) 2010.

Blogi: http://www.marcinkruszynski.blogspot.com, http://www.martinkruszynski.blogspot.com