1---
2title: Downloading media
3---
4
5ExoPlayer provides functionality to download media for offline playback. In most
6use cases it's desirable for downloads to continue even when your app is in the
7background. For these use cases your app should subclass `DownloadService`, and
8send commands to the service to add, remove and control the downloads. The
9diagram below shows the main classes that are involved.
10
11{% include figure.html url="/images/downloading.svg" index="1" caption="Classes
12for downloading media. The arrow directions indicate the flow of data."
13width="85%" %}
14
15* `DownloadService`: Wraps a `DownloadManager` and forwards commands to it. The
16  service allows the `DownloadManager` to keep running even when the app is in
17  the background.
18* `DownloadManager`: Manages multiple downloads, loading (and storing) their
19  states from (and to) a `DownloadIndex`, starting and stopping downloads based
20  on requirements such as network connectivity, and so on. To download the
21  content, the manager will typically read the data being downloaded from a
22  `HttpDataSource`, and write it into a `Cache`.
23* `DownloadIndex`: Persists the states of the downloads.
24
25## Creating a DownloadService ##
26
27To create a `DownloadService`, you need to subclass it and implement its
28abstract methods:
29
30* `getDownloadManager()`: Returns the `DownloadManager` to be used.
31* `getScheduler()`: Returns an optional `Scheduler`, which can restart the
32  service when requirements needed for pending downloads to progress are met.
33  ExoPlayer provides these implementations:
34  * `PlatformScheduler`, which uses [JobScheduler][] (Minimum API is 21). See
35    the [PlatformScheduler][] javadocs for app permission requirements.
36  * `WorkManagerScheduler`, which uses [WorkManager][].
37* `getForegroundNotification()`: Returns a notification to be displayed when the
38  service is running in the foreground. You can use
39  `DownloadNotificationHelper.buildProgressNotification` to create a
40  notification in default style.
41
42Finally, you need to define the service in your `AndroidManifest.xml` file:
43
44~~~
45<service android:name="com.myapp.MyDownloadService"
46    android:exported="false">
47  <!-- This is needed for Scheduler -->
48  <intent-filter>
49    <action android:name="com.google.android.exoplayer.downloadService.action.RESTART"/>
50    <category android:name="android.intent.category.DEFAULT"/>
51  </intent-filter>
52</service>
53~~~
54{: .language-xml}
55
56See [`DemoDownloadService`][] and [`AndroidManifest.xml`][] in the ExoPlayer
57demo app for a concrete example.
58
59## Creating a DownloadManager ##
60
61The following code snippet demonstrates how to instantiate a `DownloadManager`,
62which can be returned by `getDownloadManager()` in your `DownloadService`:
63
64~~~
65// Note: This should be a singleton in your app.
66databaseProvider = new StandaloneDatabaseProvider(context);
67
68// A download cache should not evict media, so should use a NoopCacheEvictor.
69downloadCache = new SimpleCache(
70    downloadDirectory,
71    new NoOpCacheEvictor(),
72    databaseProvider);
73
74// Create a factory for reading the data from the network.
75dataSourceFactory = new DefaultHttpDataSource.Factory();
76
77// Choose an executor for downloading data. Using Runnable::run will cause each download task to
78// download data on its own thread. Passing an executor that uses multiple threads will speed up
79// download tasks that can be split into smaller parts for parallel execution. Applications that
80// already have an executor for background downloads may wish to reuse their existing executor.
81Executor downloadExecutor = Runnable::run;
82
83// Create the download manager.
84downloadManager = new DownloadManager(
85    context,
86    databaseProvider,
87    downloadCache,
88    dataSourceFactory,
89    downloadExecutor);
90
91// Optionally, setters can be called to configure the download manager.
92downloadManager.setRequirements(requirements);
93downloadManager.setMaxParallelDownloads(3);
94~~~
95{: .language-java}
96
97See [`DemoUtil`][] in the demo app for a concrete example.
98
99The example in the demo app also imports download state from legacy `ActionFile`
100instances. This is only necessary if your app used `ActionFile` prior to
101ExoPlayer 2.10.0.
102{:.info}
103
104## Adding a download ##
105
106To add a download you need to create a `DownloadRequest` and send it to your
107`DownloadService`. For adaptive streams `DownloadHelper` can be used to help
108build a `DownloadRequest`, as described [further down this page][]. The example
109below shows how to create a download request:
110
111~~~
112DownloadRequest downloadRequest =
113    new DownloadRequest.Builder(contentId, contentUri).build();
114~~~
115{: .language-java}
116
117where `contentId` is a unique identifier for the content. In simple cases, the
118`contentUri` can often be used as the `contentId`, however apps are free to use
119whatever ID scheme best suits their use case. `DownloadRequest.Builder` also has
120some optional setters. For example, `setKeySetId` and `setData` can be used to
121set DRM and custom data that the app wishes to associate with the download,
122respectively. The content's MIME type can also be specified using `setMimeType`,
123as a hint for cases where the content type cannot be inferred from `contentUri`.
124
125Once created, the request can be sent to the `DownloadService` to add the
126download:
127
128~~~
129DownloadService.sendAddDownload(
130    context,
131    MyDownloadService.class,
132    downloadRequest,
133    /* foreground= */ false)
134~~~
135{: .language-java}
136
137where `MyDownloadService` is the app's `DownloadService` subclass, and the
138`foreground` parameter controls whether the service will be started in the
139foreground. If your app is already in the foreground then the `foreground`
140parameter should normally be set to `false`, since the `DownloadService` will
141put itself in the foreground if it determines that it has work to do.
142
143## Removing downloads ##
144
145A download can be removed by sending a remove command to the `DownloadService`,
146where `contentId` identifies the download to be removed:
147
148~~~
149DownloadService.sendRemoveDownload(
150    context,
151    MyDownloadService.class,
152    contentId,
153    /* foreground= */ false)
154~~~
155{: .language-java}
156
157You can also remove all downloaded data with
158`DownloadService.sendRemoveAllDownloads`.
159
160## Starting and stopping downloads ##
161
162A download will only progress if four conditions are met:
163
164* The download doesn't have a stop reason.
165* Downloads aren't paused.
166* The requirements for downloads to progress are met. Requirements can specify
167  constraints on the allowed network types, as well as whether the device should
168  be idle or connected to a charger.
169* The maximum number of parallel downloads is not exceeded.
170
171All of these conditions can be controlled by sending commands to your
172`DownloadService`.
173
174#### Setting and clearing download stop reasons ####
175
176It's possible to set a reason for one or all downloads being stopped:
177
178~~~
179// Set the stop reason for a single download.
180DownloadService.sendSetStopReason(
181    context,
182    MyDownloadService.class,
183    contentId,
184    stopReason,
185    /* foreground= */ false);
186
187// Clear the stop reason for a single download.
188DownloadService.sendSetStopReason(
189    context,
190    MyDownloadService.class,
191    contentId,
192    Download.STOP_REASON_NONE,
193    /* foreground= */ false);
194~~~
195{: .language-java}
196
197where `stopReason` can be any non-zero value (`Download.STOP_REASON_NONE = 0` is
198a special value meaning that the download is not stopped). Apps that have
199multiple reasons for stopping downloads can use different values to keep track
200of why each download is stopped. Setting and clearing the stop reason for all
201downloads works the same way as setting and clearing the stop reason for a
202single download, except that `contentId` should be set to `null`.
203
204Setting a stop reason does not remove a download. The partial download will be
205retained, and clearing the stop reason will cause the download to continue.
206{:.info}
207
208When a download has a non-zero stop reason, it will be in the
209`Download.STATE_STOPPED` state. Stop reasons are persisted in the
210`DownloadIndex`, and so are retained if the application process is killed and
211later restarted.
212
213#### Pausing and resuming all downloads ####
214
215All downloads can be paused and resumed as follows:
216
217~~~
218// Pause all downloads.
219DownloadService.sendPauseDownloads(
220    context,
221    MyDownloadService.class,
222    /* foreground= */ false);
223
224// Resume all downloads.
225DownloadService.sendResumeDownloads(
226    context,
227    MyDownloadService.class,
228    /* foreground= */ false);
229~~~
230{: .language-java}
231
232When downloads are paused, they will be in the `Download.STATE_QUEUED` state.
233Unlike [setting stop reasons][], this approach does not persist any state
234changes. It only affects the runtime state of the `DownloadManager`.
235
236#### Setting the requirements for downloads to progress ####
237
238[`Requirements`][] can be used to specify constraints that must be met for
239downloads to proceed. The requirements can be set by calling
240`DownloadManager.setRequirements()` when creating the `DownloadManager`, as in
241the example [above][]. They can also be changed dynamically by sending a command
242to the `DownloadService`:
243
244~~~
245// Set the download requirements.
246DownloadService.sendSetRequirements(
247    context,
248    MyDownloadService.class,
249    requirements,
250    /* foreground= */ false);
251~~~
252{: .language-java}
253
254When a download cannot proceed because the requirements are not met, it
255will be in the `Download.STATE_QUEUED` state. You can query the not met
256requirements with `DownloadManager.getNotMetRequirements()`.
257
258#### Setting the maximum number of parallel downloads ####
259
260The maximum number of parallel downloads can be set by calling
261`DownloadManager.setMaxParallelDownloads()`. This would normally be done when
262creating the `DownloadManager`, as in the example [above][].
263
264When a download cannot proceed because the maximum number of parallel downloads
265are already in progress, it will be in the `Download.STATE_QUEUED` state.
266
267## Querying downloads ##
268
269The `DownloadIndex` of a `DownloadManager` can be queried for the state of all
270downloads, including those that have completed or failed. The `DownloadIndex`
271can be obtained by calling `DownloadManager.getDownloadIndex()`. A cursor that
272iterates over all downloads can then be obtained by calling
273`DownloadIndex.getDownloads()`. Alternatively, the state of a single download
274can be queried by calling `DownloadIndex.getDownload()`.
275
276`DownloadManager` also provides `DownloadManager.getCurrentDownloads()`, which
277returns the state of current (i.e. not completed or failed) downloads only. This
278method is useful for updating notifications and other UI components that display
279the progress and status of current downloads.
280
281## Listening to downloads ##
282
283You can add a listener to `DownloadManager` to be informed when current
284downloads change state:
285
286~~~
287downloadManager.addListener(
288    new DownloadManager.Listener() {
289      // Override methods of interest here.
290    });
291~~~
292{: .language-java}
293
294See `DownloadManagerListener` in the demo app's [`DownloadTracker`][] class for
295a concrete example.
296
297Download progress updates do not trigger calls on `DownloadManager.Listener`. To
298update a UI component that shows download progress, you should periodically
299query the `DownloadManager` at your desired update rate. [`DownloadService`][]
300contains an example of this, which periodically updates the service foreground
301notification.
302{:.info}
303
304## Playing downloaded content ##
305
306Playing downloaded content is similar to playing online content, except that
307data is read from the download `Cache` instead of over the network.
308
309It's important that you do not try and read files directly from the download
310directory. Instead, use ExoPlayer library classes as described below.
311{:.info}
312
313To play downloaded content, create a `CacheDataSource.Factory` using the same
314`Cache` instance that was used for downloading, and inject it into
315`DefaultMediaSourceFactory` when building the player:
316
317~~~
318// Create a read-only cache data source factory using the download cache.
319DataSource.Factory cacheDataSourceFactory =
320    new CacheDataSource.Factory()
321        .setCache(downloadCache)
322        .setUpstreamDataSourceFactory(httpDataSourceFactory)
323        .setCacheWriteDataSinkFactory(null); // Disable writing.
324
325ExoPlayer player = new ExoPlayer.Builder(context)
326    .setMediaSourceFactory(
327        new DefaultMediaSourceFactory(cacheDataSourceFactory))
328    .build();
329~~~
330{: .language-java}
331
332If the same player instance will also be used to play non-downloaded content
333then the `CacheDataSource.Factory` should be configured as read-only to avoid
334downloading that content as well during playback.
335
336Once the player has been configured with the `CacheDataSource.Factory`, it will
337have access to the downloaded content for playback. Playing a download is then
338as simple as passing the corresponding `MediaItem` to the player. A `MediaItem`
339can be obtained from a `Download` using `Download.request.toMediaItem`, or
340directly from a `DownloadRequest` using `DownloadRequest.toMediaItem`.
341
342### MediaSource configuration ###
343
344The example above makes the download cache available for playback of all
345`MediaItem`s. It's also possible to make the download cache available for
346individual `MediaSource` instances, which can be passed directly to the player:
347
348~~~
349ProgressiveMediaSource mediaSource =
350    new ProgressiveMediaSource.Factory(cacheDataSourceFactory)
351        .createMediaSource(MediaItem.fromUri(contentUri));
352player.setMediaSource(mediaSource);
353player.prepare();
354~~~
355{: .language-java}
356
357## Downloading and playing adaptive streams ##
358
359Adaptive streams (e.g. DASH, SmoothStreaming and HLS) normally contain multiple
360media tracks. There are often multiple tracks that contain the same content in
361different qualities (e.g. SD, HD and 4K video tracks). There may also be
362multiple tracks of the same type containing different content (e.g. multiple
363audio tracks in different languages).
364
365For streaming playbacks, a track selector can be used to choose which of the
366tracks are played. Similarly, for downloading, a `DownloadHelper` can be used to
367choose which of the tracks are downloaded. Typical usage of a `DownloadHelper`
368follows these steps:
369
3701. Build a `DownloadHelper` using one of the `DownloadHelper.forMediaItem`
371   methods. Prepare the helper and wait for the callback.
372   ~~~
373   DownloadHelper downloadHelper =
374       DownloadHelper.forMediaItem(
375           context,
376           MediaItem.fromUri(contentUri),
377           new DefaultRenderersFactory(context),
378           dataSourceFactory);
379   downloadHelper.prepare(myCallback);
380   ~~~
381   {: .language-java}
3821. Optionally, inspect the default selected tracks using `getMappedTrackInfo`
383   and `getTrackSelections`, and make adjustments using `clearTrackSelections`,
384   `replaceTrackSelections` and `addTrackSelection`.
3851. Create a `DownloadRequest` for the selected tracks by calling
386   `getDownloadRequest`. The request can be passed to your `DownloadService` to
387   add the download, as described above.
3881. Release the helper using `release()`.
389
390Playback of downloaded adaptive content requires configuring the player and
391passing the corresponding `MediaItem`, as described above.
392
393When building the `MediaItem`, `MediaItem.playbackProperties.streamKeys` must be
394set to match those in the `DownloadRequest` so that the player only tries to
395play the subset of tracks that have been downloaded. Using
396`Download.request.toMediaItem` and `DownloadRequest.toMediaItem` to build the
397`MediaItem` will take care of this for you.
398
399If you see data being requested from the network when trying to play downloaded
400adaptive content, the most likely cause is that the player is trying to adapt to
401a track that was not downloaded. Ensure you've set the stream keys correctly.
402{:.info}
403
404[JobScheduler]: {{ site.android_sdk }}/android/app/job/JobScheduler
405[PlatformScheduler]: {{ site.exo_sdk }}/scheduler/PlatformScheduler.html
406[WorkManager]: https://developer.android.com/topic/libraries/architecture/workmanager/
407[`DemoDownloadService`]: {{ site.release_v2 }}/demos/main/src/main/java/com/google/android/exoplayer2/demo/DemoDownloadService.java
408[`AndroidManifest.xml`]: {{ site.release_v2 }}/demos/main/src/main/AndroidManifest.xml
409[`DemoUtil`]: {{ site.release_v2 }}/demos/main/src/main/java/com/google/android/exoplayer2/demo/DemoUtil.java
410[`DownloadTracker`]: {{ site.release_v2 }}/demos/main/src/main/java/com/google/android/exoplayer2/demo/DownloadTracker.java
411[`DownloadService`]: {{ site.release_v2 }}/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadService.java
412[`Requirements`]: {{ site.exo_sdk }}/scheduler/Requirements.html
413[further down this page]: #downloading-and-playing-adaptive-streams
414[above]: #creating-a-downloadmanager
415[setting stop reasons]: #setting-and-clearing-download-stop-reasons
416