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 WorkerThe example in the demo app also imports download state from legacy `ActionFile` 100*30877f79SAndroid Build Coastguard Workerinstances. This is only necessary if your app used `ActionFile` prior to 101*30877f79SAndroid Build Coastguard WorkerExoPlayer 2.10.0. 102*30877f79SAndroid Build Coastguard Worker{:.info} 103*30877f79SAndroid Build Coastguard Worker 104*30877f79SAndroid Build Coastguard Worker## Adding a download ## 105*30877f79SAndroid Build Coastguard Worker 106*30877f79SAndroid Build Coastguard WorkerTo add a download you need to create a `DownloadRequest` and send it to your 107*30877f79SAndroid Build Coastguard Worker`DownloadService`. For adaptive streams `DownloadHelper` can be used to help 108*30877f79SAndroid Build Coastguard Workerbuild a `DownloadRequest`, as described [further down this page][]. The example 109*30877f79SAndroid Build Coastguard Workerbelow shows how to create a download request: 110*30877f79SAndroid Build Coastguard Worker 111*30877f79SAndroid Build Coastguard Worker~~~ 112*30877f79SAndroid Build Coastguard WorkerDownloadRequest downloadRequest = 113*30877f79SAndroid Build Coastguard Worker new DownloadRequest.Builder(contentId, contentUri).build(); 114*30877f79SAndroid Build Coastguard Worker~~~ 115*30877f79SAndroid Build Coastguard Worker{: .language-java} 116*30877f79SAndroid Build Coastguard Worker 117*30877f79SAndroid Build Coastguard Workerwhere `contentId` is a unique identifier for the content. In simple cases, the 118*30877f79SAndroid Build Coastguard Worker`contentUri` can often be used as the `contentId`, however apps are free to use 119*30877f79SAndroid Build Coastguard Workerwhatever ID scheme best suits their use case. `DownloadRequest.Builder` also has 120*30877f79SAndroid Build Coastguard Workersome optional setters. For example, `setKeySetId` and `setData` can be used to 121*30877f79SAndroid Build Coastguard Workerset DRM and custom data that the app wishes to associate with the download, 122*30877f79SAndroid Build Coastguard Workerrespectively. The content's MIME type can also be specified using `setMimeType`, 123*30877f79SAndroid Build Coastguard Workeras a hint for cases where the content type cannot be inferred from `contentUri`. 124*30877f79SAndroid Build Coastguard Worker 125*30877f79SAndroid Build Coastguard WorkerOnce created, the request can be sent to the `DownloadService` to add the 126*30877f79SAndroid Build Coastguard Workerdownload: 127*30877f79SAndroid Build Coastguard Worker 128*30877f79SAndroid Build Coastguard Worker~~~ 129*30877f79SAndroid Build Coastguard WorkerDownloadService.sendAddDownload( 130*30877f79SAndroid Build Coastguard Worker context, 131*30877f79SAndroid Build Coastguard Worker MyDownloadService.class, 132*30877f79SAndroid Build Coastguard Worker downloadRequest, 133*30877f79SAndroid Build Coastguard Worker /* foreground= */ false) 134*30877f79SAndroid Build Coastguard Worker~~~ 135*30877f79SAndroid Build Coastguard Worker{: .language-java} 136*30877f79SAndroid Build Coastguard Worker 137*30877f79SAndroid Build Coastguard Workerwhere `MyDownloadService` is the app's `DownloadService` subclass, and the 138*30877f79SAndroid Build Coastguard Worker`foreground` parameter controls whether the service will be started in the 139*30877f79SAndroid Build Coastguard Workerforeground. If your app is already in the foreground then the `foreground` 140*30877f79SAndroid Build Coastguard Workerparameter should normally be set to `false`, since the `DownloadService` will 141*30877f79SAndroid Build Coastguard Workerput itself in the foreground if it determines that it has work to do. 142*30877f79SAndroid Build Coastguard Worker 143*30877f79SAndroid Build Coastguard Worker## Removing downloads ## 144*30877f79SAndroid Build Coastguard Worker 145*30877f79SAndroid Build Coastguard WorkerA download can be removed by sending a remove command to the `DownloadService`, 146*30877f79SAndroid Build Coastguard Workerwhere `contentId` identifies the download to be removed: 147*30877f79SAndroid Build Coastguard Worker 148*30877f79SAndroid Build Coastguard Worker~~~ 149*30877f79SAndroid Build Coastguard WorkerDownloadService.sendRemoveDownload( 150*30877f79SAndroid Build Coastguard Worker context, 151*30877f79SAndroid Build Coastguard Worker MyDownloadService.class, 152*30877f79SAndroid Build Coastguard Worker contentId, 153*30877f79SAndroid Build Coastguard Worker /* foreground= */ false) 154*30877f79SAndroid Build Coastguard Worker~~~ 155*30877f79SAndroid Build Coastguard Worker{: .language-java} 156*30877f79SAndroid Build Coastguard Worker 157*30877f79SAndroid Build Coastguard WorkerYou can also remove all downloaded data with 158*30877f79SAndroid Build Coastguard Worker`DownloadService.sendRemoveAllDownloads`. 159*30877f79SAndroid Build Coastguard Worker 160*30877f79SAndroid Build Coastguard Worker## Starting and stopping downloads ## 161*30877f79SAndroid Build Coastguard Worker 162*30877f79SAndroid Build Coastguard WorkerA download will only progress if four conditions are met: 163*30877f79SAndroid Build Coastguard Worker 164*30877f79SAndroid Build Coastguard Worker* The download doesn't have a stop reason. 165*30877f79SAndroid Build Coastguard Worker* Downloads aren't paused. 166*30877f79SAndroid Build Coastguard Worker* The requirements for downloads to progress are met. Requirements can specify 167*30877f79SAndroid Build Coastguard Worker constraints on the allowed network types, as well as whether the device should 168*30877f79SAndroid Build Coastguard Worker be idle or connected to a charger. 169*30877f79SAndroid Build Coastguard Worker* The maximum number of parallel downloads is not exceeded. 170*30877f79SAndroid Build Coastguard Worker 171*30877f79SAndroid Build Coastguard WorkerAll of these conditions can be controlled by sending commands to your 172*30877f79SAndroid Build Coastguard Worker`DownloadService`. 173*30877f79SAndroid Build Coastguard Worker 174*30877f79SAndroid Build Coastguard Worker#### Setting and clearing download stop reasons #### 175*30877f79SAndroid Build Coastguard Worker 176*30877f79SAndroid Build Coastguard WorkerIt's possible to set a reason for one or all downloads being stopped: 177*30877f79SAndroid Build Coastguard Worker 178*30877f79SAndroid Build Coastguard Worker~~~ 179*30877f79SAndroid Build Coastguard Worker// Set the stop reason for a single download. 180*30877f79SAndroid Build Coastguard WorkerDownloadService.sendSetStopReason( 181*30877f79SAndroid Build Coastguard Worker context, 182*30877f79SAndroid Build Coastguard Worker MyDownloadService.class, 183*30877f79SAndroid Build Coastguard Worker contentId, 184*30877f79SAndroid Build Coastguard Worker stopReason, 185*30877f79SAndroid Build Coastguard Worker /* foreground= */ false); 186*30877f79SAndroid Build Coastguard Worker 187*30877f79SAndroid Build Coastguard Worker// Clear the stop reason for a single download. 188*30877f79SAndroid Build Coastguard WorkerDownloadService.sendSetStopReason( 189*30877f79SAndroid Build Coastguard Worker context, 190*30877f79SAndroid Build Coastguard Worker MyDownloadService.class, 191*30877f79SAndroid Build Coastguard Worker contentId, 192*30877f79SAndroid Build Coastguard Worker Download.STOP_REASON_NONE, 193*30877f79SAndroid Build Coastguard Worker /* foreground= */ false); 194*30877f79SAndroid Build Coastguard Worker~~~ 195*30877f79SAndroid Build Coastguard Worker{: .language-java} 196*30877f79SAndroid Build Coastguard Worker 197*30877f79SAndroid Build Coastguard Workerwhere `stopReason` can be any non-zero value (`Download.STOP_REASON_NONE = 0` is 198*30877f79SAndroid Build Coastguard Workera special value meaning that the download is not stopped). Apps that have 199*30877f79SAndroid Build Coastguard Workermultiple reasons for stopping downloads can use different values to keep track 200*30877f79SAndroid Build Coastguard Workerof why each download is stopped. Setting and clearing the stop reason for all 201*30877f79SAndroid Build Coastguard Workerdownloads works the same way as setting and clearing the stop reason for a 202*30877f79SAndroid Build Coastguard Workersingle download, except that `contentId` should be set to `null`. 203*30877f79SAndroid Build Coastguard Worker 204*30877f79SAndroid Build Coastguard WorkerSetting a stop reason does not remove a download. The partial download will be 205*30877f79SAndroid Build Coastguard Workerretained, and clearing the stop reason will cause the download to continue. 206*30877f79SAndroid Build Coastguard Worker{:.info} 207*30877f79SAndroid Build Coastguard Worker 208*30877f79SAndroid Build Coastguard WorkerWhen a download has a non-zero stop reason, it will be in the 209*30877f79SAndroid Build Coastguard Worker`Download.STATE_STOPPED` state. Stop reasons are persisted in the 210*30877f79SAndroid Build Coastguard Worker`DownloadIndex`, and so are retained if the application process is killed and 211*30877f79SAndroid Build Coastguard Workerlater restarted. 212*30877f79SAndroid Build Coastguard Worker 213*30877f79SAndroid Build Coastguard Worker#### Pausing and resuming all downloads #### 214*30877f79SAndroid Build Coastguard Worker 215*30877f79SAndroid Build Coastguard WorkerAll downloads can be paused and resumed as follows: 216*30877f79SAndroid Build Coastguard Worker 217*30877f79SAndroid Build Coastguard Worker~~~ 218*30877f79SAndroid Build Coastguard Worker// Pause all downloads. 219*30877f79SAndroid Build Coastguard WorkerDownloadService.sendPauseDownloads( 220*30877f79SAndroid Build Coastguard Worker context, 221*30877f79SAndroid Build Coastguard Worker MyDownloadService.class, 222*30877f79SAndroid Build Coastguard Worker /* foreground= */ false); 223*30877f79SAndroid Build Coastguard Worker 224*30877f79SAndroid Build Coastguard Worker// Resume all downloads. 225*30877f79SAndroid Build Coastguard WorkerDownloadService.sendResumeDownloads( 226*30877f79SAndroid Build Coastguard Worker context, 227*30877f79SAndroid Build Coastguard Worker MyDownloadService.class, 228*30877f79SAndroid Build Coastguard Worker /* foreground= */ false); 229*30877f79SAndroid Build Coastguard Worker~~~ 230*30877f79SAndroid Build Coastguard Worker{: .language-java} 231*30877f79SAndroid Build Coastguard Worker 232*30877f79SAndroid Build Coastguard WorkerWhen downloads are paused, they will be in the `Download.STATE_QUEUED` state. 233*30877f79SAndroid Build Coastguard WorkerUnlike [setting stop reasons][], this approach does not persist any state 234*30877f79SAndroid Build Coastguard Workerchanges. It only affects the runtime state of the `DownloadManager`. 235*30877f79SAndroid Build Coastguard Worker 236*30877f79SAndroid Build Coastguard Worker#### Setting the requirements for downloads to progress #### 237*30877f79SAndroid Build Coastguard Worker 238*30877f79SAndroid Build Coastguard Worker[`Requirements`][] can be used to specify constraints that must be met for 239*30877f79SAndroid Build Coastguard Workerdownloads to proceed. The requirements can be set by calling 240*30877f79SAndroid Build Coastguard Worker`DownloadManager.setRequirements()` when creating the `DownloadManager`, as in 241*30877f79SAndroid Build Coastguard Workerthe example [above][]. They can also be changed dynamically by sending a command 242*30877f79SAndroid Build Coastguard Workerto the `DownloadService`: 243*30877f79SAndroid Build Coastguard Worker 244*30877f79SAndroid Build Coastguard Worker~~~ 245*30877f79SAndroid Build Coastguard Worker// Set the download requirements. 246*30877f79SAndroid Build Coastguard WorkerDownloadService.sendSetRequirements( 247*30877f79SAndroid Build Coastguard Worker context, 248*30877f79SAndroid Build Coastguard Worker MyDownloadService.class, 249*30877f79SAndroid Build Coastguard Worker requirements, 250*30877f79SAndroid Build Coastguard Worker /* foreground= */ false); 251*30877f79SAndroid Build Coastguard Worker~~~ 252*30877f79SAndroid Build Coastguard Worker{: .language-java} 253*30877f79SAndroid Build Coastguard Worker 254*30877f79SAndroid Build Coastguard WorkerWhen a download cannot proceed because the requirements are not met, it 255*30877f79SAndroid Build Coastguard Workerwill be in the `Download.STATE_QUEUED` state. You can query the not met 256*30877f79SAndroid Build Coastguard Workerrequirements with `DownloadManager.getNotMetRequirements()`. 257*30877f79SAndroid Build Coastguard Worker 258*30877f79SAndroid Build Coastguard Worker#### Setting the maximum number of parallel downloads #### 259*30877f79SAndroid Build Coastguard Worker 260*30877f79SAndroid Build Coastguard WorkerThe maximum number of parallel downloads can be set by calling 261*30877f79SAndroid Build Coastguard Worker`DownloadManager.setMaxParallelDownloads()`. This would normally be done when 262*30877f79SAndroid Build Coastguard Workercreating the `DownloadManager`, as in the example [above][]. 263*30877f79SAndroid Build Coastguard Worker 264*30877f79SAndroid Build Coastguard WorkerWhen a download cannot proceed because the maximum number of parallel downloads 265*30877f79SAndroid Build Coastguard Workerare already in progress, it will be in the `Download.STATE_QUEUED` state. 266*30877f79SAndroid Build Coastguard Worker 267*30877f79SAndroid Build Coastguard Worker## Querying downloads ## 268*30877f79SAndroid Build Coastguard Worker 269*30877f79SAndroid Build Coastguard WorkerThe `DownloadIndex` of a `DownloadManager` can be queried for the state of all 270*30877f79SAndroid Build Coastguard Workerdownloads, including those that have completed or failed. The `DownloadIndex` 271*30877f79SAndroid Build Coastguard Workercan be obtained by calling `DownloadManager.getDownloadIndex()`. A cursor that 272*30877f79SAndroid Build Coastguard Workeriterates over all downloads can then be obtained by calling 273*30877f79SAndroid Build Coastguard Worker`DownloadIndex.getDownloads()`. Alternatively, the state of a single download 274*30877f79SAndroid Build Coastguard Workercan be queried by calling `DownloadIndex.getDownload()`. 275*30877f79SAndroid Build Coastguard Worker 276*30877f79SAndroid Build Coastguard Worker`DownloadManager` also provides `DownloadManager.getCurrentDownloads()`, which 277*30877f79SAndroid Build Coastguard Workerreturns the state of current (i.e. not completed or failed) downloads only. This 278*30877f79SAndroid Build Coastguard Workermethod is useful for updating notifications and other UI components that display 279*30877f79SAndroid Build Coastguard Workerthe progress and status of current downloads. 280*30877f79SAndroid Build Coastguard Worker 281*30877f79SAndroid Build Coastguard Worker## Listening to downloads ## 282*30877f79SAndroid Build Coastguard Worker 283*30877f79SAndroid Build Coastguard WorkerYou can add a listener to `DownloadManager` to be informed when current 284*30877f79SAndroid Build Coastguard Workerdownloads change state: 285*30877f79SAndroid Build Coastguard Worker 286*30877f79SAndroid Build Coastguard Worker~~~ 287*30877f79SAndroid Build Coastguard WorkerdownloadManager.addListener( 288*30877f79SAndroid Build Coastguard Worker new DownloadManager.Listener() { 289*30877f79SAndroid Build Coastguard Worker // Override methods of interest here. 290*30877f79SAndroid Build Coastguard Worker }); 291*30877f79SAndroid Build Coastguard Worker~~~ 292*30877f79SAndroid Build Coastguard Worker{: .language-java} 293*30877f79SAndroid Build Coastguard Worker 294*30877f79SAndroid Build Coastguard WorkerSee `DownloadManagerListener` in the demo app's [`DownloadTracker`][] class for 295*30877f79SAndroid Build Coastguard Workera concrete example. 296*30877f79SAndroid Build Coastguard Worker 297*30877f79SAndroid Build Coastguard WorkerDownload progress updates do not trigger calls on `DownloadManager.Listener`. To 298*30877f79SAndroid Build Coastguard Workerupdate a UI component that shows download progress, you should periodically 299*30877f79SAndroid Build Coastguard Workerquery the `DownloadManager` at your desired update rate. [`DownloadService`][] 300*30877f79SAndroid Build Coastguard Workercontains an example of this, which periodically updates the service foreground 301*30877f79SAndroid Build Coastguard Workernotification. 302*30877f79SAndroid Build Coastguard Worker{:.info} 303*30877f79SAndroid Build Coastguard Worker 304*30877f79SAndroid Build Coastguard Worker## Playing downloaded content ## 305*30877f79SAndroid Build Coastguard Worker 306*30877f79SAndroid Build Coastguard WorkerPlaying downloaded content is similar to playing online content, except that 307*30877f79SAndroid Build Coastguard Workerdata is read from the download `Cache` instead of over the network. 308*30877f79SAndroid Build Coastguard Worker 309*30877f79SAndroid Build Coastguard WorkerIt's important that you do not try and read files directly from the download 310*30877f79SAndroid Build Coastguard Workerdirectory. Instead, use ExoPlayer library classes as described below. 311*30877f79SAndroid Build Coastguard Worker{:.info} 312*30877f79SAndroid Build Coastguard Worker 313*30877f79SAndroid Build Coastguard WorkerTo play downloaded content, create a `CacheDataSource.Factory` using the same 314*30877f79SAndroid Build Coastguard Worker`Cache` instance that was used for downloading, and inject it into 315*30877f79SAndroid Build Coastguard Worker`DefaultMediaSourceFactory` when building the player: 316*30877f79SAndroid Build Coastguard Worker 317*30877f79SAndroid Build Coastguard Worker~~~ 318*30877f79SAndroid Build Coastguard Worker// Create a read-only cache data source factory using the download cache. 319*30877f79SAndroid Build Coastguard WorkerDataSource.Factory cacheDataSourceFactory = 320*30877f79SAndroid Build Coastguard Worker new CacheDataSource.Factory() 321*30877f79SAndroid Build Coastguard Worker .setCache(downloadCache) 322*30877f79SAndroid Build Coastguard Worker .setUpstreamDataSourceFactory(httpDataSourceFactory) 323*30877f79SAndroid Build Coastguard Worker .setCacheWriteDataSinkFactory(null); // Disable writing. 324*30877f79SAndroid Build Coastguard Worker 325*30877f79SAndroid Build Coastguard WorkerExoPlayer player = new ExoPlayer.Builder(context) 326*30877f79SAndroid Build Coastguard Worker .setMediaSourceFactory( 327*30877f79SAndroid Build Coastguard Worker new DefaultMediaSourceFactory(cacheDataSourceFactory)) 328*30877f79SAndroid Build Coastguard Worker .build(); 329*30877f79SAndroid Build Coastguard Worker~~~ 330*30877f79SAndroid Build Coastguard Worker{: .language-java} 331*30877f79SAndroid Build Coastguard Worker 332*30877f79SAndroid Build Coastguard WorkerIf the same player instance will also be used to play non-downloaded content 333*30877f79SAndroid Build Coastguard Workerthen the `CacheDataSource.Factory` should be configured as read-only to avoid 334*30877f79SAndroid Build Coastguard Workerdownloading that content as well during playback. 335*30877f79SAndroid Build Coastguard Worker 336*30877f79SAndroid Build Coastguard WorkerOnce the player has been configured with the `CacheDataSource.Factory`, it will 337*30877f79SAndroid Build Coastguard Workerhave access to the downloaded content for playback. Playing a download is then 338*30877f79SAndroid Build Coastguard Workeras simple as passing the corresponding `MediaItem` to the player. A `MediaItem` 339*30877f79SAndroid Build Coastguard Workercan be obtained from a `Download` using `Download.request.toMediaItem`, or 340*30877f79SAndroid Build Coastguard Workerdirectly from a `DownloadRequest` using `DownloadRequest.toMediaItem`. 341*30877f79SAndroid Build Coastguard Worker 342*30877f79SAndroid Build Coastguard Worker### MediaSource configuration ### 343*30877f79SAndroid Build Coastguard Worker 344*30877f79SAndroid Build Coastguard WorkerThe example above makes the download cache available for playback of all 345*30877f79SAndroid Build Coastguard Worker`MediaItem`s. It's also possible to make the download cache available for 346*30877f79SAndroid Build Coastguard Workerindividual `MediaSource` instances, which can be passed directly to the player: 347*30877f79SAndroid Build Coastguard Worker 348*30877f79SAndroid Build Coastguard Worker~~~ 349*30877f79SAndroid Build Coastguard WorkerProgressiveMediaSource mediaSource = 350*30877f79SAndroid Build Coastguard Worker new ProgressiveMediaSource.Factory(cacheDataSourceFactory) 351*30877f79SAndroid Build Coastguard Worker .createMediaSource(MediaItem.fromUri(contentUri)); 352*30877f79SAndroid Build Coastguard Workerplayer.setMediaSource(mediaSource); 353*30877f79SAndroid Build Coastguard Workerplayer.prepare(); 354*30877f79SAndroid Build Coastguard Worker~~~ 355*30877f79SAndroid Build Coastguard Worker{: .language-java} 356*30877f79SAndroid Build Coastguard Worker 357*30877f79SAndroid Build Coastguard Worker## Downloading and playing adaptive streams ## 358*30877f79SAndroid Build Coastguard Worker 359*30877f79SAndroid Build Coastguard WorkerAdaptive streams (e.g. DASH, SmoothStreaming and HLS) normally contain multiple 360*30877f79SAndroid Build Coastguard Workermedia tracks. There are often multiple tracks that contain the same content in 361*30877f79SAndroid Build Coastguard Workerdifferent qualities (e.g. SD, HD and 4K video tracks). There may also be 362*30877f79SAndroid Build Coastguard Workermultiple tracks of the same type containing different content (e.g. multiple 363*30877f79SAndroid Build Coastguard Workeraudio tracks in different languages). 364*30877f79SAndroid Build Coastguard Worker 365*30877f79SAndroid Build Coastguard WorkerFor streaming playbacks, a track selector can be used to choose which of the 366*30877f79SAndroid Build Coastguard Workertracks are played. Similarly, for downloading, a `DownloadHelper` can be used to 367*30877f79SAndroid Build Coastguard Workerchoose which of the tracks are downloaded. Typical usage of a `DownloadHelper` 368*30877f79SAndroid Build Coastguard Workerfollows these steps: 369*30877f79SAndroid Build Coastguard Worker 370*30877f79SAndroid Build Coastguard Worker1. Build a `DownloadHelper` using one of the `DownloadHelper.forMediaItem` 371*30877f79SAndroid Build Coastguard Worker methods. Prepare the helper and wait for the callback. 372*30877f79SAndroid Build Coastguard Worker ~~~ 373*30877f79SAndroid Build Coastguard Worker DownloadHelper downloadHelper = 374*30877f79SAndroid Build Coastguard Worker DownloadHelper.forMediaItem( 375*30877f79SAndroid Build Coastguard Worker context, 376*30877f79SAndroid Build Coastguard Worker MediaItem.fromUri(contentUri), 377*30877f79SAndroid Build Coastguard Worker new DefaultRenderersFactory(context), 378*30877f79SAndroid Build Coastguard Worker dataSourceFactory); 379*30877f79SAndroid Build Coastguard Worker downloadHelper.prepare(myCallback); 380*30877f79SAndroid Build Coastguard Worker ~~~ 381*30877f79SAndroid Build Coastguard Worker {: .language-java} 382*30877f79SAndroid Build Coastguard Worker1. Optionally, inspect the default selected tracks using `getMappedTrackInfo` 383*30877f79SAndroid Build Coastguard Worker and `getTrackSelections`, and make adjustments using `clearTrackSelections`, 384*30877f79SAndroid Build Coastguard Worker `replaceTrackSelections` and `addTrackSelection`. 385*30877f79SAndroid Build Coastguard Worker1. Create a `DownloadRequest` for the selected tracks by calling 386*30877f79SAndroid Build Coastguard Worker `getDownloadRequest`. The request can be passed to your `DownloadService` to 387*30877f79SAndroid Build Coastguard Worker add the download, as described above. 388*30877f79SAndroid Build Coastguard Worker1. Release the helper using `release()`. 389*30877f79SAndroid Build Coastguard Worker 390*30877f79SAndroid Build Coastguard WorkerPlayback of downloaded adaptive content requires configuring the player and 391*30877f79SAndroid Build Coastguard Workerpassing the corresponding `MediaItem`, as described above. 392*30877f79SAndroid Build Coastguard Worker 393*30877f79SAndroid Build Coastguard WorkerWhen building the `MediaItem`, `MediaItem.playbackProperties.streamKeys` must be 394*30877f79SAndroid Build Coastguard Workerset to match those in the `DownloadRequest` so that the player only tries to 395*30877f79SAndroid Build Coastguard Workerplay the subset of tracks that have been downloaded. Using 396*30877f79SAndroid Build Coastguard Worker`Download.request.toMediaItem` and `DownloadRequest.toMediaItem` to build the 397*30877f79SAndroid Build Coastguard Worker`MediaItem` will take care of this for you. 398*30877f79SAndroid Build Coastguard Worker 399*30877f79SAndroid Build Coastguard WorkerIf you see data being requested from the network when trying to play downloaded 400*30877f79SAndroid Build Coastguard Workeradaptive content, the most likely cause is that the player is trying to adapt to 401*30877f79SAndroid Build Coastguard Workera track that was not downloaded. Ensure you've set the stream keys correctly. 402*30877f79SAndroid Build Coastguard Worker{:.info} 403*30877f79SAndroid Build Coastguard Worker 404*30877f79SAndroid Build Coastguard Worker[JobScheduler]: {{ site.android_sdk }}/android/app/job/JobScheduler 405*30877f79SAndroid Build Coastguard Worker[PlatformScheduler]: {{ site.exo_sdk }}/scheduler/PlatformScheduler.html 406*30877f79SAndroid Build Coastguard Worker[WorkManager]: https://developer.android.com/topic/libraries/architecture/workmanager/ 407*30877f79SAndroid Build Coastguard Worker[`DemoDownloadService`]: {{ site.release_v2 }}/demos/main/src/main/java/com/google/android/exoplayer2/demo/DemoDownloadService.java 408*30877f79SAndroid Build Coastguard Worker[`AndroidManifest.xml`]: {{ site.release_v2 }}/demos/main/src/main/AndroidManifest.xml 409*30877f79SAndroid Build Coastguard Worker[`DemoUtil`]: {{ site.release_v2 }}/demos/main/src/main/java/com/google/android/exoplayer2/demo/DemoUtil.java 410*30877f79SAndroid Build Coastguard Worker[`DownloadTracker`]: {{ site.release_v2 }}/demos/main/src/main/java/com/google/android/exoplayer2/demo/DownloadTracker.java 411*30877f79SAndroid Build Coastguard Worker[`DownloadService`]: {{ site.release_v2 }}/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadService.java 412*30877f79SAndroid Build Coastguard Worker[`Requirements`]: {{ site.exo_sdk }}/scheduler/Requirements.html 413*30877f79SAndroid Build Coastguard Worker[further down this page]: #downloading-and-playing-adaptive-streams 414*30877f79SAndroid Build Coastguard Worker[above]: #creating-a-downloadmanager 415*30877f79SAndroid Build Coastguard Worker[setting stop reasons]: #setting-and-clearing-download-stop-reasons 416