La mobilità innanzitutto
Rimozione definitiva in Windows Phone 7
Jaime Rodriguez
Un'ottima piattaforma mobile dovrebbe riconoscere i limiti hardware che impone la mobilità al dispositivo. Rispetto ai desktop, i dispositivi mobili dispongono di una quantità inferiore di memoria, di minore potenza di elaborazione, di uno schermo ridotto e di una durata limitata della batteria. Considerando tali limitazioni, possiamo concludere che, in un dispositivo non dedicato in cui verranno eseguite diverse applicazioni, queste applicazioni verranno chiuse o arrestate per rendere le risorse disponibili ad altre applicazioni.
Windows Phone consente di gestire tale limitazione mediante una funzionalità denominata rimozione definitiva. Benché apparentemente la rimozione definitiva sembri una soluzione semplice e immediata, in realtà è motivo di discussione tra i sviluppatori. Alcuni sostengono che non è necessaria, mentre altri che è troppo difficile. Altri ancora semplicemente odiano il nome della funzionalità. Tuttavia, le limitazioni dei dispositivi mobili la rendono indispensabile e le applicazioni mobili più importanti devono essere in grado di gestire la rimozione definitiva.
Ciclo di vita delle applicazioni Windows Phone
La maggior parte dei nuovi sviluppatori di Windows Phone passano alla piattaforma aspettandosi che il ciclo di vita di un'applicazione sia simile a quanto segue:
- Avvio
- Esecuzione
- Chiusura
- Ritorno al punto 1 e riavvio.
Windows Phone 7 introduce nuove modalità grazie a un ciclo di vita diverso meno orientato ai processi e più orientato alla sessione.
In Windows Phone 7 il ciclo di vita è più simile a quanto segue:
- Avvio
- Esecuzione
- Interruzione dell'esecuzione o chiusura
- In caso di interruzione, ritorno oppure anche in caso di interruzione, riavvio
- In caso di chiusura, riavvio
I vantaggi di questo nuovo modello orientato alla sessione sono rappresentati dal fatto che gli utenti possono spostarsi tra le applicazioni senza doversi preoccupare del modo in cui vengono gestite le risorse dal sistema operativo. L'utente non si preoccupa se l'interruzione di un gioco per rispondere a un messaggio SMS in arrivo comporterà la terminazione del processo relativo al gioco. L'utente si aspetta di poter tornare al gioco dopo aver letto il messaggio. Se la procedura funziona correttamente, i dettagli di fondo sono irrilevanti.
Gli svantaggi per gli sviluppatori sono rappresentati dal fatto che è necessario gestire un maggior numero di aspetti per offrire effettivamente la percezione di continuità della sessione poiché le sessioni sono comunque in esecuzione in un sistema operativo incentrato sui processi. Per adattare le sessioni in un panorama incentrato sui processo, è necessario creare stati logici per la sessione: Avviata, Attivata, In esecuzione, Disattivata, Contrassegnata per la rimozione definitiva e Chiusa (o Terminata).
Nella Figura 1 è illustrato il ciclo di vita pratico di un'applicazione Windows Phone 7. Gli eventi del ciclo di vita dell'applicazione, descritti nella Figura 2, vengono esposti dalla classe Microsoft.Phone.Shell.PhoneApplicationService.
Figura 1 Ciclo di vita di un'applicazione Windows Phone 7
Figura 2Eventi del ciclo di vita di un'applicazione
Stato logico | Evento PhoneApplicationService | Descrizione |
Avviata | Launching | L'applicazione viene avviata quando l'utente preme il riquadro di avvio, l'icona dell'elenco applicazioni oppure quando l'utente fa clic su una notifica popup. Lo stato Avviata rappresenta un riavvio della sessione. |
Attivata | Activated | L'applicazione viene attivata quando l'utente preme il pulsante Indietro e porta in primo piano l'applicazione precedentemente disattivata. In questo caso, l'utente si aspetta di tornare a una sessione attiva. |
In esecuzione | Running | Dopo l'avvio o l'attivazione, l'applicazione è in esecuzione. |
Disattivata | Deactivated | Un'applicazione in esecuzione viene disattivata quando l'elaborazione in primo piano viene trasferita da questa applicazione a un'altra o a un componente del sistema operativo (ad esempio, un'utilità di selezione o avvio oppure di blocco schermo). La sessione viene interrotta, ma ne è prevista la ripresa successivamente. |
Terminata (o Chiusa) | Closing | L'utente chiude l'applicazione premendo il tasto Indietro nella pagina principale. Con la chiusura, l'utente si aspetta di tornare a una nuova sessione dell'applicazione. |
Lo stato Contrassegnata per la rimozione definitiva è un po' più complicato e non direttamente correlato all'evento PhoneApplicationService. Quando un'applicazione viene disattivata, nel sistema operativo non viene immediatamente terminato il processo relativo all'applicazione. In teoria, nel sistema operativo l'applicazione viene terminata quando si verifica l'esigenza di risorse. L'applicazione non riceve alcuna notifica e viene semplicemente terminata.
In pratica, in Windows Phone 7 il processo viene terminato subito dopo che il controllo viene trasferito a un'altra applicazione in primo piano, ma non si tratta di un dettaglio necessariamente attendibile. In occasione del Mobile World Congress che si è tenuto lo scorso febbraio, Microsoft ha già annunciato che sono in arrivo miglioramenti, ad esempio il cambio rapido applicazione, pertanto è sconsigliabile fare affidamento sui dettagli di implementazione per stabilire quando si verifica la rimozione definitiva. Al contrario, è consigliabile prepararsi a implementare le corrette operazioni in fase di disattivazione di un'applicazione.
Disattivata/Contrassegnata per la rimozione definitiva
In un telefono sono in esecuzione diversi processi contemporaneamente (la shell, le funzioni telefoniche e così via), ma al massimo una sola applicazione è in esecuzione in primo piano (o zero applicazioni quando non è presente alcun processo in esecuzione in primo piano).
Quando un'applicazione in primo piano trasferisce il controllo a un'altra applicazione o a un componente del sistema operativo, viene disattivata. Dopo la disattivazione di un processo, è possibile che nel sistema operativo il processo venga terminato per rilasciare le relative risorse. Questa operazione viene definita rimozione definitiva.
Si osservi che la rimozione definitiva, benché segua sempre una disattivazione, non avviene ogniqualvolta viene disattivata un'applicazione. Infatti, Deactivated è l'ultimo evento generato da PhoneApplicationService prima della rimozione definitiva e rappresenta il momento in cui implementare una soluzione per poter riattivare un'applicazione successivamente.
Nella Figura 3 sono illustrate tutte le diverse attività che portano alla disattivazione e la probabilità che si verifichi una rimozione definitiva.
Figura 3 Attività di disattivazione
Azione | Disattivata? | Contrassegnata per la rimozione definitiva? |
L'utente preme il pulsante Indietro nella prima pagina dell'applicazione. | No. L'applicazione viene chiusa. | No. La disattivazione non si è mai verificata. |
L'utente preme il pulsante di avvio | Sì | Molto probabile, ma non è garantito. Pochi secondi dopo la disattivazione, l'applicazione viene contrassegnata per la rimozione definitiva. Se l'utente torna all'applicazione subito dopo la disattivazione, è possibile che non si verifichi la rimozione definitiva. La situazione potrebbe considerarsi come una condizione di timeout indefinito. |
L'utente richiama un'utilità di selezione o avvio che comporta una rimozione definitiva. | Sì | Molto probabile, ma si applica un timeout. |
L'utente richiama un'utilità di selezione o avvio che non comporta una rimozione definitiva. | Sì | Meno probabile, ma può comunque verificarsi. Se l'utente preme il pulsante di avvio all'interno dell'attività, viene visualizzata la regola omonima. Non viene generato un nuovo evento Deactivated perché l'applicazione è stata già disattivata. |
Viene modalità di blocco schermo e l'applicazione non è configurata per essere eseguita in questa modalità. | Sì | Molto probabile, ma si applica un timeout. |
Viene visualizzata una notifica popup che viene premuta dall'utente e viene eseguito il trasferimento a un'altra applicazione in primo piano. | Sì | Molto probabile, ma si applica un timeout. |
Esiste un sottoinsieme di utilità di selezione che non comportano una rimozione definitiva immediata. Tuttavia, può comunque avvenire se l'utente effettua un'operazione che comporta la rimozione definitiva del processo, tra cui PhotoChooserTask (a meno che l'utente non specifichi un'operazione di ritaglio), CameraCaptureTask, MediaPlayerLauncher, EmailAddressChooserTask e PhoneNumberChooserTask.
Tutte le altre utilità di selezione e avvio vengono rimosse definitivamente subito dopo che viene chiamato il metodo Show.
Per vedere il ciclo di vita di un'applicazione Windows Phone 7 in azione, avviare l'esempio LWP.TombStoning nel download del codice.
Salvataggio e ripristino degli stati
Poiché l'obiettivo di una navigazione basata sulla sessione è la semplificazione degli spostamenti tra applicazioni in primo piano, è necessario salvare tutti i relativi stati nell'evento Deactivated e ripristinarli nell'evento Activated. La maggior parte delle applicazioni dispongono di tre tipi di stati da gestire:
- Stato dell'applicazione permanente che comprende le impostazioni dell'applicazione, i dati dell'utente e così via.
- Stato dell'applicazione specifico della sessione che comprende stati temporanei, quali cache e ViewModel che devono essere ripristinati durante l'attivazione, ma che devono essere riavviati durante un nuovo avvio dell'applicazione.
- Stato specifico dell'interfaccia utente o della pagina che è necessario per ripristinare un elemento PhoneApplicationPage quando viene attivata un'applicazione. In Windows Phone 7, durante la rimozione definitiva viene salvato lo stack Indietro relativo a un'applicazione. Durante l'attivazione, viene ripristinata solo l'ultima pagina attiva prima della rimozione definitiva dell'applicazione. Se l'utente preme il pulsante Indietro, viene visualizzata la pagina precedente.
Lo stato dell'applicazione permanente deve essere salvato nell'oggetto IsolatedStorage o mediante la classe ApplicationSettings e il prima possibile, nel caso di esaurimento della batteria, ad esempio.
Se si tratta di dati dell'utente (ad esempio, la cache di una sessione) e non si desidera eseguirne la serializzazione troppo di frequente, lo stato deve essere salvato sia nell'evento Deactivated che Closing e (simmetricamente) deve essere ripristinato negli eventi Activated o Launching.
Lo stato specifico della sessione può essere salvato nella risorsa di archiviazione isolata per effettuare un controllo dei formati di serializzazione o se si dispone di troppi di dati oppure può essere salvato nel dizionario PhoneApplicationService.State. Deve essere salvato solo nell'evento Deactivated e ripristinato nell'evento Activated.
Lo stato specifico della pagina deve essere salvato nel dizionario PhoneApplicationPage.State. L'elemento fondamentale per salvare lo stato di una pagina consiste nel ricordare che l'applicazione dispone di uno stack Indietro che verrò serializzato automaticamente quando si verifica l'evento PhoneApplicationService.Deactivated. Per preparare le pagine alla rimozione definitiva, è necessario che rimangano in ascolto dell'override PhoneApplicationPage.OnNavigatedFrom all'interno della pagina e che vengano salvati gli eventuali stati di visualizzazione non ancora assegnato alla classe Model (o ViewModel) nel dizionario della pagina. È sconsigliato attendere l'evento Deactivated, perché in tal caso non sarà più possibile tornare alle pagine nello stack Indietro.
Ovviamente, se si salva lo stato della pagina quando si riceve il metodo OnNavigatedFrom, sarà necessario ripristinarlo nell'override OnNavigatedTo della pagina.
È possibile inoltre salvare lo stato specifico della pagina negli elementi ViewModel e serializzarli come stato della sessione, ma è necessario salvare lo stato non assegnato in un elemento ViewModel, alquanto sconsigliato. È opportuno sfruttare l'infrastruttura esistente in modo tale da prepararsi a ottimizzazioni successive.
Come evitare problemi
La rimozione definitiva non è un'operazione difficile. È semplicemente noiosa e richiede coerenza e pianificazione. Se l'applicazione viene disattivata ma non contrassegnata per la rimozione definitiva, lo stato rimarrà in memoria e non verrà ricreato.
Non è consigliabile affidarsi ai costruttori di classi che consentono la creazione dello stato richiesto dell'applicazione, che potrebbe essere necessario rilasciare durante la disattivazione. Si preferisce la simmetria. È preferibile utilizzare gli eventi PhoneApplicationService.Deactivated e Activated per gli stati a livello di applicazione e OnNavigatedFrom o OnNavigatedTo per gli stati della pagina.
Se sono presenti oggetti (singleton) nell'applicazione di cui viene creata un'istanza all'esterno delle chiamate di attivazione (forse a causa della creazione di un'istanza di ritardo), verificare sempre se sono stati creati e inizializzati correttamente prima di provare a utilizzarli. Un errore comune che ho riscontrato è la lettura dei dati nell'evento PhoneApplicationService.Activated o PhoneApplicationPage.OnNavigatedTo senza reimpostarli. Durante una sessione, le pagine possono essere visualizzate più volte (a prescindere dalla rimozione definitiva) e anche una sessione può essere contrassegnata per la rimozione definitiva più volte.
Dopo averlo ripristinato, è necessario cancellare uno stato. È possibile impostarlo successivamente nell'override NavigatedFrom della pagina o nell'evento Deactivated dell'applicazione.
Fare molta attenzione a ciò che si salva e a ciò che si ripristina. Per fare in modo che l'utente possa tornare facilmente all'applicazione, è necessario ripristinarla rapidamente. Se vengono salvate troppe informazioni della pagina o dello stato dell'applicazione, l'attivazione subirà rallentamenti. Quando viene attivata l'applicazione, si consiglia di sfruttare le risorse di archiviazione isolate se necessario per caricare lo stato in background che potrebbe non essere immediatamente necessario. Sia l'attivazione che la disattivazione devono essere completate in meno di 10 secondi. Occorre tenere presente tale fattore, altrimenti il processo potrebbe essere terminato dal sistema operativo prima del completamento della disattivazione o della riattivazione. L'obiettivo, comunque, da raggiungere è meno di 10 secondi.
Tenere inoltre presente le limitazioni del framework di serializzazione. È possibile, al massimo, archiviare circa 2 MB complessivi di dati della pagina e dello stato dell'applicazione. Se si supera tale limite, vengono visualizzate eccezioni durante la navigazione e la disattivazione Non è consigliato serializzare un tale quantitativo di dati nello stato della pagina. Qualora fosse necessario memorizzare nella cache set di dati di grosse dimensioni, si consiglia di salvarli nella risorsa di archiviazione isolata.
Utilizzare stringhe di query per la navigazione della pagina. Se è necessario passare il contesto in una nuova pagina, utilizzare la stringa di query passata alla pagina per passare tutti i dati o un identificatore univoco (un token) a un localizzatore di servizi che consenta di recuperare i dati in base allo stesso token. Non si creda che siano disponibili ViewModel o stati della pagina quando le pagine vengono attivate dopo essere state contrassegnate per la rimozione definitiva.
È necessario comprendere il funzionamento delle utilità di selezione e di avvio. Non tutte consentono la rimozione definitiva ed esistono regole specifiche su come associare listener di eventi per le utilità di selezione. Per ulteriori informazioni su queste regole, leggere l'articolo "How to: Use Choosers for Windows Phone" (in lingua inglese) all'indirizzo bit.ly/edEsGQ.
Tenere sempre presente la relazione tra i metodi OnNavigatedTo e PhoneApplicationPage.Loaded. In OnNavigatedTo, la struttura visiva della pagina non è ancora completamente creata. Per ripristinare lo stato dell'interfaccia utente, è spesso necessario estrarre lo stato ripristinato e attendere l'evento Loaded della pagina. Tra gli esempi di azioni che è necessario ritardare sono comprese l'impostazione dell'elemento Focus e l'impostazione dell'elemento SelectedIndex in operazioni di trasformazione tramite pivot e di scorrimento.
Se vengono eseguite numerose operazioni per il salvataggio e il ripristino dei dati (situazione sconsigliata), è necessario prendere in considerazione alcune ottimizzazione avanzate. Tenere presente che occorre attenersi a questa procedura solo se strettamente necessario e che occorre eseguire test approfonditi.
Per rilevare le rimozioni definitive, impostare un contrassegno nella classe dell'applicazione nell'evento Deactivated. Se il contrassegno non viene reimpostato con l'evento Activated, significa che non si è verificata una rimozione definitiva e che tutte le pagine dovrebbero essere ancora in memoria senza necessità di un ripristino. Per integrare questa operazione con il rilevamento della rimozione definitiva nelle pagine, è possibile utilizzare un token per ciascuna attivazione all'interno di una sessione.
Un'altra ottimizzazione consiste nel rimanere in ascolto dell'override OnNavigatingFrom della pagina e di rilevarne la direzione. Se l'enumerazione è di tipo NavigationMode.Back, la pagina verrà eliminata e non sarà necessario salvarne il relativo stato.
Anche in questo caso, la pianificazione è il fattore fondamentale per una corretta rimozione definitiva. Si consiglia di non lasciare la funzionalità di rimozione definitiva come ultima fase del ciclo di sviluppo dell'applicazione, bensì di riqualificarla. Pianificarla tempestivamente, implementarla in corso di sviluppo e sottoporla a test approfonditi.
Come ottenere un risultato eccellente
Un ultimo suggerimento per gli sviluppatori consiste nel riflettere a lungo sul modo per rendere l'esperienza semplice. I controlli di Windows Phone 7 consentono di spostarsi nella pagina in modo semplice. Per rendere l'esperienza veramente semplice per l'utente in modo che percepisca di non aver lasciato mai una pagina o un'applicazione, è necessario prendere in considerazione il ripristino di quanto segue:
- L'elemento SelectedIndex per la trasformazione tramite pivot (se contenuta nella pagina)
- La posizione di scorrimento in un elemento ListBox o eventuali altri elementi ScrollViewer nella pagina
- I livelli di zoom e altre trasformazioni in un controllo di mappe, in un visualizzatore di immagini o in qualsiasi altro controllo che supporti le modifiche
- Il testo non assegnato in un elemento TextBox; se l'elemento TextBox è fondamentale per l'applicazione (ad esempio, il testo visualizzato in un'applicazione per Twitter), sarà necessario ripristinare la selezione di testo, la posizione del cursore e lo stato attivo
- L'elemento con stato attivo in una pagina (specialmente se si tratta di un elemento TextBox di cui è necessario visualizzare il protocollo SIP
L'elemento da non ripristinare è SelectedItem all'interno di un elemento Panorama. L'elemento Panorama non supporta questo elemento e l'impostazione di un elemento DefaultItem all'interno di un elemento Panorama non corrisponde a una panoramica all'interno della pagina corretta. Si consiglia di evitare di utilizzare l'elemento DefaultItem per tornare all'elemento Panorama selezionato prima della rimozione definitiva.
Per ulteriori informazioni su questi suggerimenti, avviare l'esempio LPW.TombstoningWithState nel download del codice. Il file readme.txt contiene puntatori e script per ciascun scenario.
Alcune osservazioni finali
Questo articolo in alcun modo rappresenta un riferimento completo per la rimozione definitiva. Tuttavia, dopo aver appreso alcune informazioni su tale funzionalità, sarà possibile introdurla nelle proprie applicazioni Windows Phone 7. Sono certo che vi renderete conto immediatamente di quanto ciò migliori l'esperienza utente.
Jaime Rodriguez è un Principal Evangelist presso Microsoft che promuove l'adozione di nuove tecnologie client quali Silverlight e Windows Phone 7. È possibile contattarlo su Twitter all'indirizzo twitter.com/jaimerodriguez o leggere il suo blog all'indirizzo blogs.msdn.com/jaimer.
Un ringraziamento al seguente esperto tecnico per la revisione dell'articolo: Peter Torr