Se è vero che  sviluppare un applicazione per Windows Phone 7, conoscendo già Silverlight, non è poi così difficile in realtà ci sono una lunga serie di dettagli da conoscere perchè, non dimentichiamoci, l’applicazione non sta girando su un PC (quindi l’utilizzo corretto delle risorse diventa ancora più importante) ed è in esecuzione su un sistema operativo con caratteristiche proprie.
Una di queste è  il fatto che, almeno in questa versione, Windows Phone 7 non è un sistema operativo multi-tasking, per i vari motivi che sicuramente già conoscete.

Per lo sviluppatore di applicazioni Windows Phone 7 Il fatto di non essere multi-tasking implica un dettaglio tutt’altro che trascurabile: A differenza di ciò che accade con un applicazione WPF/Silverlight o Windows Forms dove l’applicazione inizia e termina secondo uno schema ben definito,  in Windows Phone 7 possono essere terminate ed eventualmente risvegliate più volte dal sistema operativo stesso durante la normale esecuzione Per aiutarci nel gestire questo probabile  susseguirsi di eventi di avvio, deattivazione, riattivazione e chiusura abbiamo a disposizione 4 eventi: Launched, Deactivated, Activated e Closed gentilmente messi a disposizione dalla classe PhoneApplicationService la quale grazie all’implementazione dell’interfaccia IApplicationService introdotta con Silverlight 3.0  viene gestita come LifeTimeObject dal template di Visual Studio 2010 in questo modo:

<Application.ApplicationLifetimeObjects>
        <!--Required object that handles lifetime events for the application-->
        <shell:PhoneApplicationService 
            Launching="Application_Launching" Closing="Application_Closing" 
            Activated="Application_Activated" Deactivated="Application_Deactivated"/>
    </Application.ApplicationLifetimeObjects>

dal quale si evidenzia che in App.xaml.cs il template mette  a disposizione i 4 eventi precedentemente citati.

Per capirne di più, creiamo  una nuova  applicazione Windows Phone in Visual Studio 2010 e andiamo ad aggiungere nei vari gestori  delle semplici chiamate a Debug.WriteLine così da capire come questi si susseguono:

private static string id = Guid.NewGuid().ToString();
 
 private void Application_Launching(object sender, LaunchingEventArgs e)
 {
    Debug.WriteLine("Launching {0}", App.id);
 }
 
 private void Application_Activated(object sender, ActivatedEventArgs e)
 {
    Debug.WriteLine("Activated {0}", App.id);
 }
 
 private void Application_Deactivated(object sender, DeactivatedEventArgs e)
 {
    Debug.WriteLine("Deactivated {0}", App.id);
 }
 
 private void Application_Closing(object sender, ClosingEventArgs e)
 {
    Debug.WriteLine("Closing {0}", App.id);
 }

A questo punto, premendo F5 nella output window possiamo notare come sia l’evento  Launching il primo ad essere  invocato allo startup dell’applicazione:

image

Premendo il tasto Back dell’emulatore, non essendoci nulla nella history del telefono/emulatore relativa alla nostra applicazione, questa viene terminata e nella output window è possibile vedere elencato l’evento Closing:

image

Rilanciamo l’applicazione e rivedremo di nuovo l’evento Launching (associato ad un altro Guid essendo a tutti gli effetti un altra istanza) ma ora premendo il tasto Home dell’emulatore vedremo questa volta nella output window l’evento Deactivated

image

Qual’è la differenza tra Closing e Deactivated?: In entrambi i casi l’applicazione è stata terminata e quindi scaricata dalla memoria, la differenza è che in questo caso il sistema operativo oltre a terminare l’applicazione ha anche memorizzato delle informazioni legate alla nostra applicazione, potremmo quidi dire: “Terminata con riserva” oppure usare il termine ufficiale Tombstoned.

Se ora premiamo il tasto Back dell’emulatore, essendo la nostra applicazione presente nel Back stack dell’emulatore , grazie anche alla presenza delle informazioni precedentemente salvate, può essere “resuscitata”.
Prima di controllare se ciò accade veramente e doverosa una precisazione: Un applicazione recuperata dopo un Tombstoning è, a tutti gli effetti, una nuova istanza e, visto che in questo caso abbiamo l’emulatore e il relativo debugger di mezzo, per ripristinare la stessa situazione precedente al Tombstoning è necessario attaccare il debugger, quindi per verificare che l’evento segnalato nella debug window sia Activated anzichè Launching dobbiamo:

  • Premere il tasto Back dell’emulatore, otterremo quindi che lo schermo diverrà nero.
  • Premere entro 10 secondi il tasto F5 di Visual Studio in modo che il debugger venga attaccato alla nuova istanza (10 sec è il tempo max concesso ad un applicazione per riattivarsi)

Fatto questo nella output window vedremo elencato l’evento Activated

image

Notate come il GUID sia cambiato, questo perchè l’istanza con cui abbiamo a che fare non ha nulla a che fare con quella precedente quindi tutto lo stato pre-Tombstoning è andato completamente perso.
La domanda ora è : se in entrambi i casi l’applicazione viene terminata e scaricata dalla memoria qual’è la differenza tra Terminating e Deactivated?

Potremmo dire che in caso di Deactivated  “Non tutto è perduto”: Innanzitutto è vero che a tutti gli effetti viene rilanciata una nuova istanza dell’applicazione ma questa, grazie alle informazioni memorizzate in precedenza, è in grado ad esempio di istanziare automaticamente la pagina attiva nel momento del TombStoning.
Verifichiamolo andando ad aggiungere alla nostra applicazione di prova  una nuova pagina RunTask:

image

e aggiungiamo alla pagina principale un pulsante che ci permetterà di navigare alla pagina stessa:

image

 

 

private void OnRunTask(object sender, RoutedEventArgs e)
{
  Uri uri = new Uri("/RunTask.xaml", UriKind.Relative);
  NavigationService.Navigate(uri);
}

Ora rilanciamo l’applicazione premendo F5, premiamo il pulsante RunTask e, mentre siamo nella pagina RunTask premiamo il tasto Home dell’emulatore: L’applicazione verrà deattivata (TombStoned)
Premiamo ora il tasto Back dell’emulatore per ripristinare l’applicazione, ricordando di premere F5 in Visual Studio entro 10 secondi non appena lo schermo diventa nero: Non solo vedremo l’evento Activated ma vedremo apparire non la pagina principale ma direttamente la pagina RunTask a conferma che la pagina attualmente aperta è tra le informazioni memorizzate in caso di Tombstoning.

Prima di proseguire un altro paio di precisazioni:

  • In caso di riattivazioni le eventuali finestre aperte in precedenza non vengono più automaticamente istanziate.
  • Se l’utente esegue una lunga serie di operazioni percui il  Back stack viene riempito, può essere che, premendo ripetutamente il tasto Back, l’applicazione non venga mai più ripristinata.

Le applicazioni Windows Phone 7 devono perciò essere pensate per lavorare secondo questo modo “transiente”.

Supponiamo che la pagina RunTask abbia al proprio interno una TextBox che permette di inserire il termine che vogliamo cercare nel MarketPlace, ovvero qualcosa tipo:

image
namespace Wp7ApplicationLifeCycle
{
   using Microsoft.Phone.Tasks;
 
   public partial class RunTask : PhoneApplicationPage
   {
      public RunTask()
      {
         InitializeComponent();
      }
 
      private void OnClick(object sender, RoutedEventArgs e)
      {
         MarketplaceSearchTask marketplaceSearchTask = new MarketplaceSearchTask();
         marketplaceSearchTask.SearchTerms = txtSearch.Text;
         marketplaceSearchTask.Show();
      }
   }
}

Rilanciamo l’applicazione, navighiamo di nuovo verso RunTask, scriviamo qualcosa all’interno della TextBox, premiamo il tasto Home dell’emulatore e, successivamente, premiamo il tasto Back per riattivarla. Risultato: Siamo di nuovo nella pagina RunTask ma il contenuto della TextBox cosi come altri eventuali dati inerenti allo stato dell’applicazione stessa sono andati persi.Sad smile
Un altro aspetto importante è il fatto che, tra le cause che possono causare il TombStoning di un applicazione puo esserci anche il nostro stesso codice il quale, può ad esempio invocare dei Launchers o Chooser ovvero dei tasks il cui compito è quello di eseguire delle azioni o interagire con l’utente, tra questi, ad esempio, quello che permette all’utente di selezionare una foto tra quelle memorizzate nel device.

Essendo queste, passatemi il termine, “Finestre di dialogo” dei programmi a tutti gli effetti il risultato per la nostra applicazione è esattamente quello di prima, ovvero l’effetto Tombstoning e la perdita delle informazioni di stato non appena un qualsiasi task viene invocato.
Ad esempio, nel nostro caso premedo il tasto Search si ha: l’attivazione del MarketplaceSearchTask, la disattivazione della nostra applicazione e il fatto che, premendo subito il tasto Back dell’emulatore la textbox appare vuota.
Decisamente non piacevole, sopratutto per l’utente finale al quale poco importa di cosa succede alla nostra applicazione.Confused smile

E quindi? Dobbiamo strutturare l’applicazione affinchè, quando deattivata, persista le informazioni necessarie per un suo corretto ripristino durante l’eventuale riattivazione facendo credere all’utente che l’applicazione non sia mai stata interrotta.
Fortunatamente nel framework di Windows Phone abbiamo due classi che ci aiutano in questo: PhoneApplicationService  e PhoneApplicationPage le quali espongono una proprietà State di tipo IDictionary<string,object> che viene salvata/riletta durante la fase di Tombstoning e successiva riattivazione permettendoci quindi di associare a questa sessione le informazioni transienti.

Le guidelines indicano di usare:

  • PhoneApplicationPage per gestire informazioni legate allo stato della pagina.
  • PhoneApplicationService per gestire info legate all’intera applicazione all’interno degli eventi Deactivated/Activated.

All’interno del dictionary esposto dalla proprietà State di entrambe le classi è possibile associare qualsiasi tipo purchè serializzabile.

Aggiungendo nella pagina RunTask il seguente codice:

private const string Key = "SearchText";
 
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
   base.OnNavigatedFrom(e);
   if (this.State.ContainsKey(Key))
   {
      txtSearch.Text = this.State[Key].ToString();
      this.State.Remove(Key);
   }
}
 
protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e)
{
   base.OnNavigatedTo(e);
   if (!string.IsNullOrEmpty(txtSearch.Text))
   {
      this.State.Add(Key, txtSearch.Text);
   }
}

andiamo a salvare/leggere il contenuto della textbox col risultato che, in caso di Tombstoning, alla successiva riattivazione la pagina viene riproposta nello stesso stato in cui l’utente l’aveva lasciata il quale sarà convinto che dietro le quinte non sia avvenuto nulla mentre, in realtà, di cose ne sono accadute parecchie Smile.

Ff817008.5e84773d-ae0d-43b8-a956-ffdac77b2922(en-us,VS.92).png

Quando inizierete a pensare alla vostra prossima applicazione Windows Phone 7, ricordatevi quindi che avrete a che fare con un applicazione che potrà nascere e morire parecchie volte e sopratutto, almeno fino a quando le cose non cambieranno, scordatevi di avere a che fare con applicazioni tipicamente stateful.

Il diagramma a fianco descrive in maniera dettagliata I vari stati in cui un applicazione Windows Phone 7 può trovarsi e come vanno gestiti.

 

…cosa ci tocca fare per risparmiare un po’ di batteria…. Just kidding

 

Technorati Tags: ,