BroadcastReceiver / DownloadManager

DownloadManager – Part 1

A fairly common operation in Android apps is the need to download content to the device. While it is easy enough to construct a network call to download the required content, there is also a built in way which can be particularly useful if the content being downloaded should also be shared with other apps, or stored in a publicly accessible location on the device. In this short series of articles we’ll take a look at DownloadManager which can solve many of the common problems in a really easy to use API.

downloadBefore we dive in, it is work mentioning that DownloadManager has been around since API 9. Although there have been a couple of small API tweaks since then, the API has largely been stable since then. Anything that we cover which isn’t available back to API 9 will be mentioned when we cover it.

Let’s begin by creating a simple Activity which will will encapsulate our UI. It consists of a single button which, when clicked, will download a PDF file to the device.

The important bits to note here are the Downloader object which is where all of the DownloadManager logic is housed and there is also a callback method named fileDownloaded() which will get called when a download successfully completes. One thing also worth noting is that we call the unregister() methods on the Downloader in onDestroy() more on this in due course.

Hopefully that is all pretty straightforward, but we’ll return to this later on once we’ve explored how DownloadManager does its magic and understand better what we’re doing in fileDownloaded().

So let’s dive in to the Downloader class to see how we actually use DownloadManager:

We get an instance of DownloadManager by calling getSystemService(Context.DOWNLOAD_SERVICE) on a Context.

Initiating a basic download is simplicity itself and is performed in the download() method. We first register a BroadcastReceiver (more on this later) which will handle callbacks from the DownloadManager once the download completes. Then we construct a DownloadManager.Request from the supplied Uri. Finally we enqueue the request to DownloadManager which returns a unique ID which identifies our download request and we’re done.

We can cancel the download request my removing from the DownloadManager using the ID – this is performed in cancel().

The unregister() method which we saw the Activity calling in its onDestroy() method actually unregisters the BroadcastReceiver. The reason that this is important is that we have no guarantee that DownloadManager will actually complete the download before the user exits the app. This is one of the benefits of using DownloadManager – we don’t need to create background Services to perform the download – DownloadManager does that for us. By unregistering the receiver when the Activity is destroyed we ensure that we won’t attempt to do anything with the completed download if it finishes when the app is not active.

The downloadComplete() method will get called by the BroadcastReceiver when the download actually completes. We first check that it matches the id of our download and, if so, we query the DownloadManager to get the results of the download. If we supported multiple concurrent downloads we could specify multiple download IDs in the query, so a Cursor is returned which allows us to iterate through them. In our case we’ll only permit a single download, but the sample code works for multiple files as well.

We first extract the status field which indicates the status of the download – in our case this will only get triggered once a download completes so we just need to check whether the download was successful, however in a full implementation it may be useful to query download manager on resuming the app to update the status of any downloads which may have been started in a previous app session.

if the download was successful then we extract the local Uri (where the download was stored) and the MIME type of the content, and pass this back to the listener (in our case the Activity).

Let’s now take a look at the BroadcastReceiver:

There’s nothing much to see here. The main things worth noting are that the action that we need to register for is DownloadManager.ACTION_DOWNLOAD_COMPLETE and then we’ll get woken for download completion events. We can register for other events such as the user clicking on the download notification so that we could bring the user in to our app to control see or cancel the download, but we’re only interested in completions.

The other thing worthy of note is that when we receive the completion Intent, we extract the downloadId from the Intent extras.

Finally let’s turn our attention back to the Activity and look at what we do when we receive the callback once the download completes.

The content in question is actually a PDF file. Rather thasn build in a PDF view to the app, I’ve elected to let other apps on the hard work, and use an implicit Intent to allow the system to determine an appropriate app to view the content.

The important thing which facilitates that is the Uri that DownloadManager returns is actually a content://... Uri which is very friendly to other apps andf allows them to easily access the content through as ContentProvider provided by DownloadManager.

So DownloadManager enables us to download content and not have to worry about implementing the background handling ourselves, and also permits really easy mechanisms to share that content with other apps. All for some relatively simple and straightforward code.

If we run this we can see a notification appear while the download is in progress, and then we get prompted by the OS to select a PDF view within which to view the content. When we chose one, the content is displayed:

basic_operation

In the next article we’ll explore DownloadManager in greater depth and see some more of powerful features.

The source code for this article is available here.

© 2016, Mark Allison. All rights reserved.

CC BY-NC-SA 4.0 DownloadManager – Part 1 by Styling Android is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. Permissions beyond the scope of this license may be available at http://blog.stylingandroid.com/license-information.

Leave a Reply

Your email address will not be published. Required fields are marked *

*