It’s common for an application to want to download (or upload) content from the internet possibly with a view to caching that content so that a user doesn’t have to wait to access it in the future.
Internet Explorer is a prime example of this where I’m browsing the web and I download some videos and documents and zip files and so on and store them on my hard-drive. When I start a download, I select a file for the downloaded content and then while the download is on-going, I can continue to browse the web and IE (via the CTRL+J shortcut) can show me a status window for my downloads;
where I can take relevant actions like pausing, resuming, cancelling and so on.
If I close down IE then it automatically pauses my downloads and I can resume them when I run up the browser again.
This sort of functionality is useful to any kind of app and would be particularly hard to build for a Windows 8 Metro style app without some additional runtime help.
For reasons of responsiveness, predictability and effective battery management a Metro style app has its process lifecycle closely managed such that if the app is not currently running in the foreground then, after a short period of time, all activity within the app (in terms of threads running code) is suspended.
If the app were performing background downloads or uploads, this would be a problem.
However, WinRT has explicit support for background downloads and uploads via the BackgroundDownloader and BackgroundUploader classes that live in Windows.Networking.BackgroundTransfer.
This is a subset of the full background task functionality that’s available to a Metro style app.
I put together a small sample around the BackgroundDownloader a few months ago and I was talking to someone about it the other day so thought I’d share. It’s a simple HTML/JS app which displays a UI like the one below;
In this screenshot you can see that the app offers the user the chance to download from a URL (in this case it’s the Big Buck Bunny movie but that URL can be changed) and store that download into a local file (in the application’s own local folder).
In the screenshot, I’ve started 2 downloads both going to two different local files – movie1.mp4 and movie2.mp4.
If I go to the resource manager on Windows then I see that it is not my process which is doing this network IO;
Instead, the IO is happening in another process BackgroundTransferHost.exe and so if my process was to suspend as it is here on this screenshot;
Then I still see my downloads continuing and even if my process is terminated from suspension by the operating system (which I can simulate in Task Manager by waiting until it suspends and then killing it) then I still see the BackgroundTransferHost.exe process continuing my downloads and when I run the app back up the UI reconnects to the running downloads and shows their status again;
However, if I were to;
- Explicitly close the application (either with ALT+F4 or by dragging the app from the top of the screen to the bottom).
- Log out of the machine or restart it.
then my downloads would be cancelled and the next time I run the app, it tries to show this as in;
This is a fundamental difference between the system suspending or terminating the app which allows the downloads to continue and the user terminating the app which does not allow the downloads to continue.
Note – I’d always advise testing these kinds of scenarios from outside of the debugger because (by design) the debugger interferes with when an application will suspend/resume.
The Background Download Code
Note, this sample code uses some convenience shortenings of namespaces from WinRT;
var nsBackground = Windows.Networking.BackgroundTransfer; var nsStorage = Windows.Storage; var nsPopups = Windows.UI.Popups;
In terms of the JavaScript code that sits in this sample when the user clicks on the button labelled “start download” I run a little code;
function onStartDownload() { // Make a new background downloader. var downloader = new nsBackground.BackgroundDownloader(); var promise; // Set the cost policy for free networking only. downloader.costPolicy = nsBackground.BackgroundTransferCostPolicy.avoidNetworkCharges; // Create the file that the user specified in the UI within the local // folder for the app. promise = WinJS.Application.local.folder.createFileAsync( txtLocalFile.value, nsStorage.CreationCollisionOption.replaceExisting); promise.done( function (file) { // Create the download. var download = downloader.createDownload( new Windows.Foundation.Uri(txtUrlDownload.value), file); // Put it into our UI list, adding event handlers etc. var entry = makeListEntryFromDownload(download, false) bindingList.push(WinJS.Binding.as(entry)); }, function () { showError({ message: "failed to create file" }); } ); }
Hopefully, that’s fairly self-explanatory. We create the local file that the user specified and we use a new BackgroundDownloader to createDownload from a URL into that file and we put the details into our list on the screen.
BackgroundDownloader.createDownload() returns a DownloadOperation to represent the new download and it’s also possible for an app to get a list of any existing operations by using BackgroundDownloader.getCurrentDownloadsAsync() and that again returns a list of DownloadOperation and my app attempts this when it first runs up;
function buildDownloadsListAsync() { var promise = nsBackground.BackgroundDownloader.getCurrentDownloadsAsync().then( function (downloads) { var failedCount = 0; if (downloads) { downloads.forEach( function (download) { try { var entry = makeListEntryFromDownload(download, true); bindingList.push(WinJS.Binding.as(entry)); } catch (ex) { failedCount++; } } ); } return (failedCount); } ); return (promise); }
Here the app is trying to find any existing downloads that are still “alive” and put them into its UI when it first runs up and make a note of any existing downloads that seem to have been cancelled.
The function makeListEntryFromDownload is used from both of the functions I’ve listed here and it is as below;
function makeListEntryFromDownload(download, existing) { // This object is what we will put into our UI for binding. var entry = {}; // Grab the details from the DownloadOperation entry.guid = download.guid; entry.uri = download.requestedUri.rawUri; entry.file = download.resultFile.name; entry.progress = (download.progress.bytesReceived / download.progress.totalBytesToReceive) * 100.0; // If this is an existing download rather than a new one then we attach. if (existing) { entry.promise = download.attachAsync(); } else { // Otherwise, we start it off. entry.promise = download.startAsync(); } // The DownloadOperation has a promise to tell us about completion, // progress and errors. entry.promise = entry.promise.then( function () { downloadComplete(entry, download.resultFile); }, function (e) { downloadError(entry, e); }, function () { downloadProgress(entry, download); } ); // If we need to cancel then we capture the cancel method from the promise. entry.cancelDownload = function (e) { entry.promise.cancel(); } return (entry); }
The interesting thing to me here is the use of the WinJS.Promise to represent the completion/progress/error of the download operation and how the function either uses DownloadOperation.startAsync() to get that WinJS.Promise if the download is a new download and uses DownloadOperation.attachAsync() if the download is an existing one recovered when the application has been suspended and terminated and then re-run by the user.
That’s about all I put into this sample at the time of writing it. I’m sure it’s not perfect but if it’s of any use to you then feel free to download from here and use it to aid your own work around background downloads and uploads in Windows 8 Metro style apps – I think it’s a pretty vital feature to have given how the OS suspends/resumes apps to preserve resources.