ExoPlayer 提供下载媒体以供离线播放的功能。大多数
用例,最好即使您的应用处于
背景。对于这些用例,您的应用应创建 DownloadService 和
向该服务发送添加、删除和控制下载内容的命令。通过
下图显示了涉及的主要类。
DownloadService:封装DownloadManager并将命令转发给它。通过 服务可让DownloadManager即使在应用处于运行状态时也保持运行 背景。DownloadManager:管理多个下载内容,以及加载(和存储)其 状态包括从(和到)DownloadIndex,根据 网络连接等要求要下载 则管理器通常会读取从HttpDataSource,并将其写入Cache。DownloadIndex:保留下载状态。
创建 DownloadService
如需创建 DownloadService,请将其子类化并实现其
抽象方法:
getDownloadManager():返回要使用的DownloadManager。getScheduler():返回一个可选的Scheduler,它可能会重启 服务。 ExoPlayer 提供以下实现: <ph type="x-smartling-placeholder">- </ph>
PlatformScheduler,它使用 JobScheduler(最低 API 为 21)。请参阅 有关应用权限要求的 PlatformScheduler javadocs。WorkManagerScheduler,使用 WorkManager。
getForegroundNotification():返回在 服务正在前台运行。您可以使用DownloadNotificationHelper.buildProgressNotification,用于创建 默认样式的通知。
最后,在 AndroidManifest.xml 文件中定义该服务:
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC"/>
<application>
<service android:name="com.myapp.MyDownloadService"
android:exported="false"
android:foregroundServiceType="dataSync">
<!-- This is needed for Scheduler -->
<intent-filter>
<action android:name="androidx.media3.exoplayer.downloadService.action.RESTART"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</service>
</application>
请参阅 ExoPlayer 中的 DemoDownloadService 和 AndroidManifest.xml
了解具体示例。
创建 DownloadManager
以下代码段演示了如何实例化 DownloadManager,
在您的 DownloadService 中,由 getDownloadManager() 返回:
Kotlin
// Note: This should be a singleton in your app. val databaseProvider = StandaloneDatabaseProvider(context) // A download cache should not evict media, so should use a NoopCacheEvictor. val downloadCache = SimpleCache(downloadDirectory, NoOpCacheEvictor(), databaseProvider) // Create a factory for reading the data from the network. val dataSourceFactory = DefaultHttpDataSource.Factory() // Choose an executor for downloading data. Using Runnable::run will cause each download task to // download data on its own thread. Passing an executor that uses multiple threads will speed up // download tasks that can be split into smaller parts for parallel execution. Applications that // already have an executor for background downloads may wish to reuse their existing executor. val downloadExecutor = Executor(Runnable::run) // Create the download manager. val downloadManager = DownloadManager(context, databaseProvider, downloadCache, dataSourceFactory, downloadExecutor) // Optionally, properties can be assigned to configure the download manager. downloadManager.requirements = requirements downloadManager.maxParallelDownloads = 3
Java
// Note: This should be a singleton in your app. databaseProvider = new StandaloneDatabaseProvider(context); // A download cache should not evict media, so should use a NoopCacheEvictor. downloadCache = new SimpleCache(downloadDirectory, new NoOpCacheEvictor(), databaseProvider); // Create a factory for reading the data from the network. dataSourceFactory = new DefaultHttpDataSource.Factory(); // Choose an executor for downloading data. Using Runnable::run will cause each download task to // download data on its own thread. Passing an executor that uses multiple threads will speed up // download tasks that can be split into smaller parts for parallel execution. Applications that // already have an executor for background downloads may wish to reuse their existing executor. Executor downloadExecutor = Runnable::run; // Create the download manager. downloadManager = new DownloadManager( context, databaseProvider, downloadCache, dataSourceFactory, downloadExecutor); // Optionally, setters can be called to configure the download manager. downloadManager.setRequirements(requirements); downloadManager.setMaxParallelDownloads(3);
如需查看具体示例,请参阅演示版应用中的 DemoUtil。
添加下载内容
要添加下载内容,请创建一个DownloadRequest并将其发送到
DownloadService。对于自适应信息流,请使用 DownloadHelper 来帮助
构建 DownloadRequest。以下
下例显示了如何创建下载请求:
Kotlin
val downloadRequest = DownloadRequest.Builder(contentId, contentUri).build()
Java
DownloadRequest downloadRequest = new DownloadRequest.Builder(contentId, contentUri).build();
在此示例中,contentId 是内容的唯一标识符。在简单的情况下,
contentUri 通常可以用作 contentId,但应用可以免费使用
找到最适合他们的用例的 ID 方案。DownloadRequest.Builder还有
一些可选的 setter 方法。例如,setKeySetId 和 setData 可用于
设置应用希望与下载内容关联的 DRM 和自定义数据;
。内容的 MIME 类型也可以使用 setMimeType 指定,
,针对无法从 contentUri 推断出内容类型的提示。
创建完成后,系统会将请求发送到 DownloadService 以添加
下载:
Kotlin
DownloadService.sendAddDownload( context, MyDownloadService::class.java, downloadRequest, /* foreground= */ false )
Java
DownloadService.sendAddDownload( context, MyDownloadService.class, downloadRequest, /* foreground= */ false);
在此示例中,MyDownloadService 是应用的 DownloadService 子类,
foreground 参数用于控制服务是否会在
前景。如果您的应用已在前台运行,则 foreground
参数通常应设置为 false,因为 DownloadService 会
如果它确定自己有工作要做,则将其置于前台。
正在移除下载内容
您可以向 DownloadService 发送 remove 命令,以移除下载内容。
其中 contentId 标识要移除的下载内容:
Kotlin
DownloadService.sendRemoveDownload( context, MyDownloadService::class.java, contentId, /* foreground= */ false )
Java
DownloadService.sendRemoveDownload( context, MyDownloadService.class, contentId, /* foreground= */ false);
您还可以使用
DownloadService.sendRemoveAllDownloads。
开始和停止下载
只有在满足以下四个条件时,系统才会开始下载:
- 下载没有停止原因。
- 下载未暂停。
- 已满足继续下载的要求。要求可以指定 以及设备是否应 处于闲置状态或已连接到充电器。
- 未超出并行下载数量上限。
所有这些条件都可以通过将命令发送至您的
DownloadService。
设置和清除下载停止原因
您可以设定一个或所有下载停止的原因:
Kotlin
// Set the stop reason for a single download. DownloadService.sendSetStopReason( context, MyDownloadService::class.java, contentId, stopReason, /* foreground= */ false ) // Clear the stop reason for a single download. DownloadService.sendSetStopReason( context, MyDownloadService::class.java, contentId, Download.STOP_REASON_NONE, /* foreground= */ false )
Java
// Set the stop reason for a single download. DownloadService.sendSetStopReason( context, MyDownloadService.class, contentId, stopReason, /* foreground= */ false); // Clear the stop reason for a single download. DownloadService.sendSetStopReason( context, MyDownloadService.class, contentId, Download.STOP_REASON_NONE, /* foreground= */ false);
stopReason 可以是任何非零值(Download.STOP_REASON_NONE = 0 为
一个特殊值,表示下载不会停止)。符合以下条件的应用:
停止下载的原因有多种,您可以使用不同的值来跟踪
每次下载停止的原因设置和清除全部的停止原因
下载操作与设置和清除
只不过应将 contentId 设置为 null。
如果下载因非零停止原因,
Download.STATE_STOPPED 状态。停止原因将保存在
DownloadIndex,因此在应用进程被终止时会保留,并且
后来又重新启动了
暂停和恢复所有下载内容
可以通过以下方式暂停和恢复所有下载:
Kotlin
// Pause all downloads. DownloadService.sendPauseDownloads( context, MyDownloadService::class.java, /* foreground= */ false ) // Resume all downloads. DownloadService.sendResumeDownloads( context, MyDownloadService::class.java, /* foreground= */ false )
Java
// Pause all downloads. DownloadService.sendPauseDownloads(context, MyDownloadService.class, /* foreground= */ false); // Resume all downloads. DownloadService.sendResumeDownloads(context, MyDownloadService.class, /* foreground= */ false);
下载暂停后,它们将处于 Download.STATE_QUEUED 状态。
与设置停止原因不同,此方法不会保留任何状态
更改。它只会影响 DownloadManager 的运行时状态。
设置继续下载的要求
Requirements 可用于指定模型必须满足的限制条件
继续操作。可通过调用
DownloadManager.setRequirements()(在创建 DownloadManager 时指定),如
请参阅上述示例。也可以通过发送命令
发送到 DownloadService:
Kotlin
// Set the download requirements. DownloadService.sendSetRequirements( context, MyDownloadService::class.java, requirements, /* foreground= */ false)
Java
// Set the download requirements. DownloadService.sendSetRequirements( context, MyDownloadService.class, requirements, /* foreground= */ false);
如果下载因不满足要求而无法继续,则会
将处于 Download.STATE_QUEUED 状态。您可以查询不符合
DownloadManager.getNotMetRequirements() 的要求。
设置并行下载数量上限
可通过调用
DownloadManager.setMaxParallelDownloads()。这通常需要
创建 DownloadManager,如以上示例所示。
当下载因并行下载数量达到上限而无法继续时
已在进行中,将处于Download.STATE_QUEUED状态。
查询下载内容
可以查询 DownloadManager 的 DownloadIndex,以获取所有
包括已完成或已失败的下载内容。DownloadIndex
可通过调用 DownloadManager.getDownloadIndex() 获取。一个光标
然后调用
DownloadIndex.getDownloads()。单次下载的状态
可通过调用 DownloadIndex.getDownload() 进行查询。
DownloadManager 还提供 DownloadManager.getCurrentDownloads(),
用于仅返回当前下载的状态(即未完成或失败)。这个
方法对于更新通知以及显示的其他界面组件非常有用
当前下载的进度和状态
收听下载内容
您可以向 DownloadManager 添加一个监听器,以便在发生当前事件时收到通知
下载内容更改状态:
Kotlin
downloadManager.addListener( object : DownloadManager.Listener { // Override methods of interest here. } )
Java
downloadManager.addListener( new DownloadManager.Listener() { // Override methods of interest here. });
请参阅演示版应用的 DownloadTracker 类中的 DownloadManagerListener,了解
一个具体示例。
播放已下载的内容
播放下载内容与播放在线内容类似,不同之处在于
数据是从下载内容 Cache 中读取的,而不是通过网络读取。
要播放已下载的内容,请使用以下代码创建 CacheDataSource.Factory:
Cache 实例,并将其注入
DefaultMediaSourceFactory:
Kotlin
// Create a read-only cache data source factory using the download cache. val cacheDataSourceFactory: DataSource.Factory = CacheDataSource.Factory() .setCache(downloadCache) .setUpstreamDataSourceFactory(httpDataSourceFactory) .setCacheWriteDataSinkFactory(null) // Disable writing. val player = ExoPlayer.Builder(context) .setMediaSourceFactory( DefaultMediaSourceFactory(context).setDataSourceFactory(cacheDataSourceFactory) ) .build()
Java
// Create a read-only cache data source factory using the download cache. DataSource.Factory cacheDataSourceFactory = new CacheDataSource.Factory() .setCache(downloadCache) .setUpstreamDataSourceFactory(httpDataSourceFactory) .setCacheWriteDataSinkFactory(null); // Disable writing. ExoPlayer player = new ExoPlayer.Builder(context) .setMediaSourceFactory( new DefaultMediaSourceFactory(context).setDataSourceFactory(cacheDataSourceFactory)) .build();
如果同一个播放器实例也将用于播放未下载的内容
那么应将 CacheDataSource.Factory 配置为只读,以避免
以及下载相应内容。
使用 CacheDataSource.Factory 配置播放器后,它将
访问已下载的内容以进行播放。然后播放下载的内容
就像将相应的 MediaItem 传递给播放器一样简单。MediaItem
可以使用 Download.request.toMediaItem 从 Download 获取,或
从 DownloadRequest 直接使用 DownloadRequest.toMediaItem。
MediaSource 配置
上面的示例将下载缓存设置为可播放所有
MediaItem。您也可以将下载缓存
单独的 MediaSource 实例,可直接传递给播放器:
Kotlin
val mediaSource = ProgressiveMediaSource.Factory(cacheDataSourceFactory) .createMediaSource(MediaItem.fromUri(contentUri)) player.setMediaSource(mediaSource) player.prepare()
Java
ProgressiveMediaSource mediaSource = new ProgressiveMediaSource.Factory(cacheDataSourceFactory) .createMediaSource(MediaItem.fromUri(contentUri)); player.setMediaSource(mediaSource); player.prepare();
下载和播放自适应流
自适应视频流(例如 DASH、SmoothStreaming 和 HLS)通常包含 媒体轨道。系统中通常有多个轨道包含相同的内容 (例如标清、高清和 4K 视频轨道)。还有 包含不同内容的同一类型的多个轨道(例如,多个 不同语言的音轨)。
对于流式播放,可以使用曲目选择器来选择
曲目。同样,若要下载,可以使用 DownloadHelper
选择要下载哪些曲目。DownloadHelper 的典型用法
具体步骤如下:
- 使用以下任一
DownloadHelper.forMediaItem构建DownloadHelper方法。准备帮助程序并等待回调。Kotlin
val downloadHelper = DownloadHelper.forMediaItem( context, MediaItem.fromUri(contentUri), DefaultRenderersFactory(context), dataSourceFactory ) downloadHelper.prepare(callback)
Java
DownloadHelper downloadHelper = DownloadHelper.forMediaItem( context, MediaItem.fromUri(contentUri), new DefaultRenderersFactory(context), dataSourceFactory); downloadHelper.prepare(callback);
- (可选)使用
getMappedTrackInfo检查默认的所选轨道 和getTrackSelections,然后使用clearTrackSelections进行调整replaceTrackSelections和addTrackSelection。 - 通过调用以下方法来为所选轨道创建
DownloadRequest:getDownloadRequest。您可以将该请求传递给DownloadService, 添加下载内容(如上所述)。 - 使用
release()释放帮助程序。
若要播放已下载的自适应内容,您需要配置播放器和
传递相应的 MediaItem,如上所述。
构建 MediaItem 时,MediaItem.localConfiguration.streamKeys 必须为
设置为与 DownloadRequest 中的请求相匹配,以便播放器仅尝试
播放已下载的部分曲目。使用
Download.request.toMediaItem 和 DownloadRequest.toMediaItem,用于构建
MediaItem 将为您处理。