13

Jan

Il multitasking in Mango: i background transfers – Un esempio pratico

Ci siamo lasciati nel post precedente parlando della classe BackgroundTransferRequest, che permette di fare download e upload di dati anche ad applicazione chiusa. Abbiamo visto le proprietà esposte dall’oggetto, come possiamo configurarlo e quali eventi abbiamo a disposizione per gestire il ciclo di vita della richiesta.

Ora, come promesso, vediamo un po’ di codice concreto: nel post precedente abbiamo semplicemente inizializzato il trasferimento e lo abbiamo avviato. In questo articolo vedremo invece come gestire gli eventi messi a disposizione dall’oggetto e come gestire il fatto che il download potrebbe terminare mentre l’applicazione è chiusa.

L’applicazione che andiamo a realizzare è un semplice player audio, in grado di riprodurre musica sfruttando l’oggetto MediaElement. La canzone da riprodurre, però, verrà scaricata nell’Isolated Storage tramite la classe BackgroundTransferRequest: una volta completato il download, la canzone verrà riprodotta in automatico.

L’interfaccia della nostra applicazione

Partiamo dall’interfaccia, che vediamo nell’esempio:


      
      
      
      

Gli elementi che la compongono sono:

  • L’oggetto MediaElement vero e proprio, che useremo per la riproduzione audio.
  • Una ProgressBar, che mostrerà lo stato del download in maniera visuale.
  • Un TextBlock, che mostrerà lo stato del download in maniera testuale (byte trasferiti / byte totali).
  • Un pulsante, che darà l’avvio al download.

Gli eventi TransferProgressChanged e TransferStatusChanged

Nel post precedente ci siamo sottoscritti a questi due eventi, senza però entrare nel dettaglio implementativo. Partiamo dall’evento TransferProgressChanged, che viene scatenato nel momento in cui la quantità di dati trasferita è cambiata.

void backgroundRequest_TransferProgressChanged(object sender, BackgroundTransferEventArgs e)
      {
      double received = backgroundRequest.BytesReceived;
      double total = backgroundRequest.TotalBytesToReceive;
      double percentage = (received/total)*100;

      LoadingBar.Value = percentage;

      Progress.Text = string.Format("{0} - {1}", received, total);
      }

L’utilizzo tipico questo evento è quello di gestire un indicatore di download, che informi l’utente dello stato dell’operazione: noi non siamo da meno, perciò utilizziamo le informazioni esposte dalla classe BackgroundTransferRequest per riempire la nostra progress bar. Per fare questo, ci basta utilizzare due proprietà esposte dall’oggetto, ovvero BytesReceived (la quantità di byte trasferita) e TotalBytesToReceive (la dimensione totale in byte del file da scaricare). Occhio che questo evento è asincrono: se il trasferimento è veloce, può capitare perciò che venga richiamato più volte in poco tempo. Ecco perchè, prima di aggiornare la progress bar, andiamo a salvare le informazioni sul download in delle variabili locali: in caso contrario, andremmo sempre a recuperare il valore più recente e non riusciremmo a vedere il riempimento graduale della barra.

Vediamo ora invece quello che succede quando viene scatenato l’evento TransferStatusChanged.

void backgroundRequest_TransferStatusChanged(object sender, BackgroundTransferEventArgs e)
      {
      if (backgroundRequest.TransferStatus == TransferStatus.Completed)
      PlayAudio();
      }


      private void PlayAudio()
      {
      using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication())
      {
      using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream(Path.Combine("shared/transfers", "Cocaine.mp3"), FileMode.Open, FileAccess.Read, store))
      {
      MediaSong.SetSource(stream);
      MediaSong.Play();
      }
      }
      }

Banalmente andiamo a controllare lo stato della richiesta: se questa è completata allora andiamo a invocare il metodo PlayAudio che, come possiamo vedere sotto, non fa altro che recuperare il file dall’Isolated Storage, leggerlo come stream di dati e assegnarlo come Source dell’oggetto MediaElement.

Il download in background

L’utente potrebbe però lanciare il download ed uscire dall’applicazione: in quel caso, dato che il processo è chiuso, non siamo più in grado di ricevere e gestire gli eventi TransferStatusChanged e TransferProgressChanged. Come facciamo perciò a riprodurre la nostra canzone una volta che l’utente è tornato nell’applicazione?

Il trucco sta nell’agganciarci all’evento OnNavigatedTo che ormai conosciamo molto bene e verificare qual è lo stato corrente del trasferimento, come nell’esempio:

protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
      {
      if (backgroundRequest!=null && backgroundRequest.TransferStatus==TransferStatus.Completed)
      PlayAudio();
      }

In questo modo, se nel momento in cui l’utente rientra nell’applicazione il download è già terminato, la musica viene automaticamente riprodotta. Un’altra cosa che potremmo fare, nel caso in cui il download invece sia ancora in corso, è aggiornare la progress bar, così da riflettere il vero stato del download.

Il BackgroundTransferService

Come spiegato nel post precedente, il BackgroundTransferService è il servizio di scheduling che si occupa di gestire i trasferimenti in background. Analogamente a quanto possiamo fare con il ScheduledActionService, possiamo utilizzarlo per gestire tutta una serie di operazioni che possono risultare molto utili. Pensiamo ad un’applicazione mandata dallo stato Dormant allo stato Tombstoned (ricordate i post riguardo il nuovo ciclo di vita delle applicazioni in Mango?). In questo caso, essendo il processo terminato, al restore l’istanza dell’oggetto (o degli oggetti) di tipo BackgroundTransferRequest non esiste più: dobbiamo perciò recuperarlo tramite il BackgroundTransferService.

Per gestire questo tipo di situazioni ogni BackgroundTransferRequest è identificato da un id univoco, che viene assegnato all’istanza nel momento in cui questa viene creata. Tale id, di tipo string, è memorizzato nella proprietà RequestId: passando tale identificatore al metodo Find esposto dal BackgroundTransferService possiamo recuperare i download in corso nella nostra applicazione. Un esempio di scenario di utilizzo è memorizzare questi id nell’isolated storage, così da averli a disposizione anche in caso di tombstone per recuperare le informazioni sui trasferimenti in corso.

In conclusione

Abbiamo visto in dettaglio la nuova classe BackgroundTransferRequest, che può essere utilizzata in alternativa a WebClient e HttpWebRequest per gestire i trasferimenti di dati da Internet verso la nostra applicazione. Di seguito trovate il link per scaricare il progetto di esempio del quale vi ho mostrato qualche esempio di codice nel corso di questi due post.

by Il blog di Matteo Pagani on 1/13/2012
Post archive