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