Nella lezione precedente abbiamo esaminato il funzionamento del sistema di sospensione di un'applicazione Windows Store da parte di Windows RT. Abbiamo anche visto che il runtime conceda alle nostre applicazioni un tempo relativamente breve per completare le operazioni precedenti alla sospensione, pena la chiusura forzata dell'applicazione stessa. Si tratta di una scelta che, per quanto radicale, ha tuttavia una sua logica: se lo scopo della sospensione è appunto quello di liberare risorse quando le applicazioni non utilizzate dall'utente si trovano in background, permettere a un'applicazione di "rimandare" la sospensione impegnandosi in operazioni "time-consuming" avrebbe rischiato di compromettere il risultato desiderato.
Per nostra fortuna, però, Windows RT prevede un meccanismo per "ritardare" la sospensione, (permettendoci così di salvare dei dati temporanei tramite un servizio web o sul cloud, ovvero di compiere altre operazioni che potrebbero impiegare più tempo del previsto) a patto però di informare il sistema operativo che stiamo eseguendo un'operazione asincrona.
Riprendiamo la nostra solution come l'abbiamo lasciata alla fine della lezione precedente e torniamo per un attimo all'implementazione di default del metodo OnSuspending (lo riportiamo di seguito per comodità):
private void OnSuspending(object sender, SuspendingEventArgs e)
{
var deferral = e.SuspendingOperation.GetDeferral();
//TODO: Save application state and stop any background activity
deferral.Complete();
}
Il meccanismo è invero piuttosto semplice: con la chiamata al metodo SuspendingOperation.GetDeferral informiamo il sistema operativo che stiamo eseguendo una chiamata asincrona per salvare i nostri dati temporanei, mentre il metodo Complete notifica al sistema operativo che la nostra applicazione ha terminato le operazioni di salvataggio ed è pronta per essere sospesa. Infine, da menzionare la possibilità di interrogare il runtime per conoscere il tempo rimanente per concludere l'operazione (espresso come DateTimeOffSet), accessibile mediante la proprietà Deadline.
Vediamone il funzionamento con alcune semplici linee di codice con finalità puramente didascaliche (della serie "don't try it at home!"). Modifichiamo il metodo OnSuspending come segue:
private void OnSuspending(object sender, SuspendingEventArgs e)
{
var deferral = e.SuspendingOperation.GetDeferral();
// parte da aggiungere
var settingsValues = Windows.Storage.ApplicationData.Current.LocalSettings.Values;
if (settingsValues.ContainsKey("LastSuspensionTime"))
{
settingsValues.Remove("LastSuspensionTime");
}
settingsValues.Add("LastSuspensionTime", DateTime.Now.ToString());
// fine aggiunta
deferral.Complete();
}
Quello che facciamo, dopo aver informato il sistema operativo che stiamo cominciando un'operazione asincrona (SuspendingOperation.GetDeferral
), è inserire (o aggiornare, se già presente) una chiave, denominata LastSuspensionTime e contente l'orario corrente, all'interno della proprietà LocalSettings
della classe ApplicationData
.
La classe LocalSettings permette allo sviluppatore di salvare semplici coppie chiave/valore nello storage locale. Come abbiamo già spiegato in un altro articolo in questa guida, infatti, Windows RT non permette alle applicazioni Windows Store di accedere al filesystem, limitandosi a mettere a loro disposizione uno storage locale (LocalSettings) che per molti versi ricorda l'IsolatedStorage di Windows Phone 7.
In alternativa allo storage locale è possibile sfruttare la proprietà RoamingSettings per condividere i dati fra più dispositivi, salvandoli su uno storage cloud-based (e individuato dall'account Windows LiveID dell'utente correte).
Una volta completate le operazioni di salvataggio, informiamo Windows RT che l'applicazione è pronta ad essere sospesa mediante il metodo Complete. Nella prossima parte, vedremo come recuperare le informazioni temporaneamente salvate nello storage locale al momento della sospensione.
Prima di effettuare un deployment dell'applicazione, modifichiamo il costruttore della classe App.xam.cs in modo da ripristinare l'originario handler dell'evento di sospensione:
public App()
{
this.InitializeComponent();
this.Suspending += OnSuspending;
//this.Suspending += AppSuspending; // al posto della precedente commentata
}
È importante ricordare ancora una volta che la sospensione, di per sé, non richiede il salvataggio di alcun dato temporaneo, in quanto la memoria occupata dall'applicazione viene mantenuta intatta per tutta la durata della sospensione. Tuttavia, dal momento che il runtime può in qualsiasi momento terminare un'applicazione in background per liberare risorse di sistema, e che la chiusura dell'applicazione avviene senza nessuna notifica, l'evento di sospensione rappresenta per lo sviluppatore l'ultimo momento utile per salvare dati temporanei.
Ciò detto, una volta che la nostra applicazione è stata sospesa dal runtime (ma non terminata), qualora l'utente torni nuovamente sull'applicazione (mediante ALT+TAB
, oppure cliccando sul relativo tile nella Start Page), Windows RT ci notificherà l'evento di riprisino, permettendoci così di compiere le operazioni necessarie in vista del ripristino dello stato originario.
Anche in questo caso però occorre chiarire che, di per sé, il resuming di un'applicazione non richiede alcuna operazione particolare: se, ad esempio, prima della sospensione l'utente ha lasciato un form parzialmente compilato, al momento del ripristino dell'applicazione lo ritroverà esattamente nelle stesse condizioni.
Non è tuttavia difficile immaginare contesti applicativi in cui sia importante, quanto meno sotto il profilo della user experience, aggiornare lo stato lasciato al momento della sospensione. Immaginate, ad esempio, un'applicazione finanziaria che mostri l'andamento della borsa, un rss reader, o una qualunque applicazione che visualizzi dati suscettibili di variare rapidamente nel tempo: dal momento che la sospensione di un'applicazione può durare per un tempo indeterminato (fino a quando cioè il sistema non decide di terminarla per liberare risorse ovvero l'utente non chiude espressamente l'applicazione), al momento del ripristino dell'applicazione potrebbe essere opportuno riproporre all'utente dei dati aggiornati al posto di quelli ormai "obsoleti".
Proviamo a vedere come funziona il meccanismo di resuming con un semplice esempio. Apriamo nuovamente la solution creata nell'articolo precedente e aggiungiamo due textbox nel codice XAML della MainPage per mostrare, rispettivamente, l'orario in cui è avvenuta la sospensione e quello del resuming dell'applicazione :
<Page
x:Class="Html.it.Demo.Windows8.ALM.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Html.it.Demo.Windows8.ALM"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<StackPanel Orientation="Vertical" VerticalAlignment="Top" Margin="10,01,10,10">
<TextBlock Name="suspendTime" FontSize="48" Margin="10,10,10,10" />
<TextBlock Name="resumeTime" FontSize="48" Margin="10,10,10,10" />
</StackPanel>
</Grid>
</Page>
Modifichiamo quindi il costruttore della classe MainPage.xaml.cs
in modo da gestire l'evento di ripristino successivo alla sospensione:
public MainPage()
{
this.InitializeComponent();
App.Current.Resuming += Current_Resuming;
}
Adesso possiamo implementare il relativo handler come segue:
private void Current_Resuming(object sender, object e)
{
this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal,
() =>
{
var settingsValues = Windows.Storage.ApplicationData.Current.LocalSettings.Values;
if (settingsValues.ContainsKey("LastSuspensionTime"))
{
suspendTime.Text = "Suspension time: " + settingsValues["LastSuspensionTime"].ToString();
}
resumeTime.Text = "Resuming time:" + DateTime.Now.ToString();
});
}
Come si vede il codice è sostanzialmente speculare a quello usato nel metodo OnSuspending, solo che in questo caso leggiamo (anziché scrivere) il valore associato alla chiave LastSuspensionTime salvata in precedenza nei LocalSettings della nostra applicazione (valore che, come si ricorderà dall'esempio precedente, indica l'orario in cui il runtime ha sospeso la nostra applicazione) e mostrato a video assieme all'orario in cui è avvenuto il ripristino.
Per testarne il funzionamento, effettuate un nuovo deployment dell'applicazione, lanciatela cliccando sul tile e mettetela in background aprendo altre applicazioni Windows Store (o, in alternativa, premete F5
e cliccate sul pulsante Suspend nella toolbar di Visual Studio 2012 per simulare la sospensione).
Se ora tornate sull'applicazione (se siete in modalità debug, in Visual Studio 2012 cliccate sul pulsante Resume nella toolbar per ripristinare l'applicazione) dovreste ottenere qualcosa di simile a quanto mostrato nella prossima immagine:
Come si è visto in questo lungo excursus sul ciclo di vita di un'applicazione Windows Store, le novità introdotte da Windows RT rispetto alle classiche (e ancora attuali) applicazioni Win32 sono tante e significative.
- Le applicazioni vengono sospese dal sistema operativo se non più in foreground;
- Non può esistere più di una instanza in memoria della stessa applicazione;
- Possono esistere al massimo due applicazioni in foreground se l'utente effettua lo snap di una applicazione affianco all'altra;
- Il sistema operativo ripristina una applicazione nel momento in cui l'utente la riporta in foreground;
- Il sistema operativo può terminare una applicazione sospesa se ha bisogno di memoria.