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