Welcome to part 5 of this series on background processing in Windows 8 applications. In this part, we are going to explore an option to transfer files in the background. With the specifics that surround the process lifecycle of Windows 8, downloading or uploading a file in a Windows app isn’t possible without a specific option in the framework. This option is often referred to as the Background Transfer API. This API is the focus of this article.
The need for the Background Transfer API
In part 1 of this series, we have covered the process lifecycle of Windows 8 Store apps. If you remember correctly, by default only one Windows 8 app can be running in the foreground. When the app isn’t running in the foreground any longer, the app will be suspended (after 5 seconds). If you’re building an app that has to transfer files (upload or download), your transfer will be suspended so you’re basically locking the user in your application as long as the transfer in running. I don’t think a lot of users will be happy with this experience!
The solution for this problem is the Background Transfer API. Using this API, it becomes possible to allow apps to register transfers with a separate service.
The API explained
The API allows us to register downloads or uploads with a separate service instead of allowing the transfer to happen from your application. When using this API, you’ll see a separate service being created, running as a separate process, as can be seen in the image below. The process is named after the name of the application performing the transfer.
However, if we take a look at the details, we can see that in fact the process is called BackgroundTransferHost.exe. An instance of this exe is created for each application that performs a download or upload.
Thanks for this service, the download won’t be suspended when the application is going into suspended mode. Indeed, I’m saying here Suspended mode. The process won’t be interrupted if the application is suspended but it will be killed if the application gets terminated, even if it’s being terminated because of memory reasons. Therefore, when the device reboots, the download is cancelled as well. The same goes for an application crash. For the Windows Phone developers among us, this may come as a surprise. WP has a similar mechanism to allow downloading outside of the application. But in WP, the downloads “survive” device reboots.
All classes that have to do with the Background Transfer API are living in the Windows.Networking.BackgroundTransfer namespace. The most relevant class for downloading is the BackgroundDownloader class (for uploading, you can probably guess it is the BackgroundUploader class). These classes support transferring files over http and https. The BackgroundDownloader also support ftp tranfers.
Downloading a file
Let’s take a look at how we can download a file using the BackgroundDownloader class. If we think about a download, it’s basically defined by 2 things: the file we want to download and the file we want to create locally. In the sample application, we can specify the file we want to download, as can be seen below.
In code, we’ll create a file on the local disk. We can use the following code to create a file name from the URI:
1: private string CreateFileName(Uri uri)
2: {
3: return Path.GetFileName(uri.LocalPath);
4: }
Once we have the file name, we can create the download. A download is defined by a DownloadOperation instance. We can create an instance of this class as follows:
1: private async Task<DownloadOperation> CreateDownload(Uri uri, string fileName)
2: {
3: StorageFile storageFile = await
4: KnownFolders.VideosLibrary.CreateFileAsync(fileName, CreationCollisionOption.GenerateUniqueName);
5: BackgroundDownloader downloader = new BackgroundDownloader();
6:
7: downloader.CostPolicy = BackgroundTransferCostPolicy.UnrestrictedOnly;
8:
9: return downloader.CreateDownload(uri, storageFile);
10:
11: }
In the code above, I’m using the CreateFileAsync() method, passing in the file name and the location. This StorageFile instance is not a physical file, it’s merely a representation of a file. Note that this is an async operation of course, since it involves IO. Next, we instantiate the BackgroundDownloader class and call on to the CreateDownload(), passing in the file we want to download and the StorageFile (in other words, where to put the file). This call returns us a DownloadOperation. The download hasn’t started yet at this point though.
To start the actual download of the file, we call on to the StartAsync of the DownloadOperation as follows:
1: var download = await CreateDownload(uri, fileName);
2:
3: await download.StartAsync()
4: .AsTask(cancellationTokenSource.Token, downloadProgressCallback);
Note that we are calling the AsTask(), passing in 2 more parameters. AsTask returns a Task that represents the ongoing operation.
The first parameter, cancellationTokenSource.Token, gets the Token associated with the CancellationTokenSource. Uhm, what? Well, to put it simple, it’s possible to cancel a download. However, a download happens async. To cancel an async task, we use the CancellationTokenSource.Token, which allows us to signal that the (or better: all) async tasks associated with this token, should be cancelled. To actually cancel a download, we can use the following code:
1: private void CancelAllDownloadsButton_Click_1(object sender, RoutedEventArgs e)
2: {
3: cancellationTokenSource.Cancel();
4: cancellationTokenSource.Dispose();
5:
6: cancellationTokenSource = new CancellationTokenSource();
7: }
The second parameter, downloadProgressCallback, is defined as follows;
1: Progress<DownloadOperation> downloadProgressCallback =
2: new Progress<DownloadOperation>(UpdateDownloadProgress1);
This provides an IProgress<T>, that will invoke the callback (UpdateDownloadProgress1) every time progress is being reported. So basically, this allows us to specify what needs to happen when there progress being reported from the transfer. In our case, we are updating a progress bar in the UI.
1: private void UpdateDownloadProgress1(DownloadOperation downloadOperation)
2: {
3: if (downloadOperation.Progress.TotalBytesToReceive > 0)
4: {
5: var ignore = this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
6: {
7: DownloadProgressBar1.Value =
8: downloadOperation.Progress.BytesReceived * 100
9: / downloadOperation.Progress.TotalBytesToReceive;
10: });
11: }
12:
13: }
Since this code runs in a background thread, you will need to use the Dispatcher to update UI elements, as can be seen above. It’s of course required that the main application is running in the foreground, otherwise we can’t update the UI since the app wouldn’t be getting any CPU assigned to it. If the main app isn’t in the foreground, this code will simply have no effect; in other words, you won’t get any error or have to add any checks to see if the main app is effectively in the foreground.
With this code, we can download a file from any URI to the local storage of the Windows 8 application. Even if the application enters suspended mode, the download will continue.
But as mentioned, when the application is terminated, the download will not continue, since the separate process is being terminated as well. However, it would be a good experience for the user that all what had been downloaded so far isn’t gone (remember that the default experience in Windows 8 is that the user should not be aware that the application is being terminated; the app should try to restore all state). This is possible, let’s take a look at how we can do that next.
First, we need to retrieve the “paused” downloads. We can do that using the BackgroundDownloader class again which defines a GetCurrentDownloadsAsync() method. Once we have this List<DownloadOperation> (which is the return type of this class), for each DownloadOperation, we call the AttachAsync() method. This method will resume the download. Since this is async, we can loop over all paused downloads and start them all together in one go. The result is concurrent downloads running within the application.
1: private async Task RetrieveActiveDownloads()
2: {
3: try
4: {
5: List<Task> downloadTasks = new List<Task>();
6:
7: var downloads = await BackgroundDownloader.GetCurrentDownloadsAsync();
8:
9: foreach (var download in downloads)
10: {
11: downloadTasks.Add(ResumeDownload(download));
12: }
13:
14: await Task.WhenAll(downloadTasks);
15: }
16: catch (Exception)
17: { }
18: }
19:
20: private async Task ResumeDownload(DownloadOperation downloadOperation)
21:
22: {
23:
24: Progress<DownloadOperation> downloadProgressCallback =
25:
26: new Progress<DownloadOperation>(ResumeDownloadProgress);
27:
28: await downloadOperation.AttachAsync()
29:
30: .AsTask(cancellationTokenSource.Token, downloadProgressCallback);
31:
32: }
If we execute this code, we can see that we have multiple downloads happening at the same time.
To finish this article, let’s take a look at uploading files.
Uploading a single file
Uploading a file has the same “issues” as downloading a file: when the application gets suspended, the upload that we do from the application itself will get cancelled. Instead, we have to use the separate process here too. To upload, we are going to use the BackgroundUploader class, and we can use the CreateUpload() method on the BackgroundUploader to create an UploadOperation. On the UploadOperation, we can then call the StartAsync() to start the upload. Uploads too can be cancelled and can report progress in the exact same way as we saw with with downloading.
The code below performs an upload. Note that we can also use the SetRequestHeader to pass/set specific headers in the request. We can also pass credentials if needed using the ServerCredential property. This was also possible with the BackgroundDownloader.
1: Progress<UploadOperation> uploadProgressCallback =
2: new Progress<UploadOperation>(UploadProgress);
3:
4: BackgroundUploader backgroundUploader = new BackgroundUploader();
5: backgroundUploader.SetRequestHeader("Filename", selectedFile.Name);
6:
7: var uploadOperation =
8: backgroundUploader.CreateUpload
9: (new Uri("http://posttestserver.com/post.php", UriKind.Absolute), selectedFile);
10:
11: var result = await uploadOperation.StartAsync()
12: .AsTask(uploadCancellationTokenSource.Token, uploadProgressCallback);
It’s also possible to upload more than one file in just one upload operation. The file to-be-uploaded are then placed in a list and all these files will be uploaded after one another. For this, we have the BackgroundTransferContentPart class. For each file we want to upload, we are going to create such an instance. We then pass this list of BackgroundTransferContentPart instances to the BackgroundUploader. This is shown in the code below.
1: var selectedFiles = await picker.PickMultipleFilesAsync();
2:
3: if (selectedFiles != null)
4: {
5: var parts = new List<BackgroundTransferContentPart>();
6:
7: foreach (var file in selectedFiles)
8: {
9: var part = new BackgroundTransferContentPart();
10: part.SetFile(file);
11: parts.Add(part);
12: }
13:
14: Progress<UploadOperation> uploadProgressCallback =
15: new Progress<UploadOperation>(UploadProgress);
16:
17: BackgroundUploader backgroundUploader = new BackgroundUploader();
18:
19: var uploadOperation = await backgroundUploader.CreateUploadAsync
20: (new Uri("http://posttestserver.com/post.php", UriKind.Absolute), parts);
21:
22: var result = await uploadOperation.StartAsync()
23: .AsTask(uploadCancellationTokenSource.Token, uploadProgressCallback);
24: }
Summary
In this part, we’ve extensively covered the Background Transfer API to allow our apps to upload and download files while not the main application on screen. This way, we improve the user experience: the user can do other stuff while the download is taking place.
In the 6th and final part, we will look at playing audio from the background. Hope to see you there!
About the author
Gill Cleeren is Microsoft Regional Director, Silverlight MVP, Pluralsight trainer and Telerik MVP. He lives in Belgium where he works as .NET architect at Ordina. Gill has given many sessions, webcasts and trainings on new as well as existing technologies, such as Silverlight, ASP.NET and WPF at conferences including TechEd, TechDays, DevDays, NDC Oslo, SQL Server Saturday Switserland, Silverlight Roadshow in Sweden, Telerik RoadShow UK… Gill has written 2 books: “Silverlight 4 Data and Services Cookbook” and Silverlight 5 Data and Services Cookbook and is author of many articles for magazines and websites. You can find his blog at www.snowball.be. Twitter: @gillcleeren