Friday, June 17, 2011

Background transfers in Mango

This is a third article in a series on Mango background agents. Background transfers lets your application initiate file uploads and/or downloads which continue in the background even after the application is closed.
For transfers, there is no need to create a separate library as the BackgroundTransferService will handle the work for us. We simply create a background transfer request and queue it up with the service. Here’s an example for downloading a file from a remote Url :
   1: var request = new BackgroundTransferRequest(new Uri(downloadUrl, UriKind.Absolute));
   2: request.Method = "GET";
   3: request.DownloadLocation = new Uri("/transfers/" + filename, UriKind.RelativeOrAbsolute);
   4: BackgroundTransferService.Add(request);

Notice the “/transfers/” path in the DownloadLocation property value. The OS always downloads files (or uploads them from) the “transfers” directory  at the root of isolated storage. The directory is created at app installation, and shouldn’t be deleted.

In a real-world app, you should wrap your Add() call in a try-catch.

So you can see it’s very easy to initiate a background transfer. However, after launching a transfer, you’ll typically need to do a few common thing, such as :

- Monitor progress and update the UI accordingly
- Cancel a transfer
- Do something with a completed transfer
- Reconnect to ongoing transfers, if any, after closing then relaunching the app

To monitor progress, you handle the TransferProgressChanged event of the BackgroundTransferRequest. In the handler, you can retrieve the number of bytes received (or sent) and the total number of bytes to receive (or send) from the Request property of the eventargs, and based on these numbers, you can update the visual state of a progress bar for example.

   1: progbar.Maximum = e.Request.TotalBytesToReceive;
   2: progbar.Value = e.Request.BytesReceived;

Note that if you’re displaying a list of transfers in the UI with a progress bar for each transfer, you’ll need to find a way to tie each progress bar with its associated request. One way is to use the RequestId property of the request as a marker, for example you can set the Tag property of each progress bar to the request ID for its associated request.

To cancel a request, you first retrieve that request using the Find method of the BackgroundTransferService using the request ID, and then you call Remove() :

   1: var req = BackgroundTransferService.Find(requestId);
   2: if (req != null)
   3: {
   4:     try
   5:     {
   6:         BackgroundTransferService.Remove(req);
   7:     }
   8:     catch (Exception ex)
   9:     {
  10:         MessageBox.Show("Error cancelling request : " + ex.Message);
  11:     }
  12: }

To process a completed request, you can handle the TransferStatusChanged event of the BackgroundTransferRequest :

   1: request.TransferStatusChanged += request_TransferStatusChanged;

If the transfer is complete and no transfer error occured, you should remove the completed request from the queue, and then perform any custom work :

   1: void request_TransferStatusChanged(object sender, BackgroundTransferEventArgs e)
   2: {
   3:     if (e.Request.TransferStatus == TransferStatus.Completed)
   4:     {
   5:         if (e.Request.TransferError != null) 
   6:         {
   7:             //Handle transfer error scenario
   8:         }
   9:         else
  10:         {
  11:             //So some work, e.g. open downloaded file
  12:         }
  13:     }
  14: }

Finally, to reconnect our app to background transfers after the app has been closed and relaunched, we can use the OnNavigatedTo method. Here we retrieve the queued transfers using the BackgroundTransferService’s Requests property, and we rebind our UI to the requests. If we’re using custom objects instead of binding directly to the requests, we first need to re-attach our reconstructed objects to the requests, typically by examining the requestId property of the requests. We also need to reattach the event handlers (TransferProgressChanged and TransferStatusChanged) in the method.

To wrap up, the background transfer API provides convenient abstractions that we easily use for typical scenarios. Combined with data binding, the API makes it a breaze to allow downloads and uploads in our application.

Happy transfering!

No comments:

Post a Comment