ExoPlayer 的 缓存-- 一 使用简介
ExoPlayer 提供了媒体离线下载功能。在大多数用例中,即使应用程序在后台,也可以继续下载。应用实现这个工呢,应用程序应该继承子类DownloadService
并实例化,并向DownloadService发送命令以添加、删除和控制下载。下图显示了涉及的主要类。
图 1.下载媒体的类。箭头方向表示数据流向。
-
DownloadService
: 包装 一个DownloadManager
并将命令转发给它。DownloadManager
即使应用程序在后台,该服务也允许继续运行。 -
DownloadManager
:管理多个下载,并使用DownloadIndex 加载 存储它们的状态,根据网络连接等要求启动和停止下载等。要下载内容,DownloadManager通常会读取从 HttpDataSource 下载的数据 ,并将其写入Cache
. -
DownloadIndex
:保存下载状态。
创建下载服务
要创建DownloadService
,需要对其进行子类化并实现其抽象方法:
getDownloadManager()
:返回要使用的DownloadManager
。-
getScheduler(): 返回一个可选 Scheduler
当满足挂起下载进度所需的要求时,它可以重新启动服务。ExoPlayer 提供了这些实现:
-
PlatformScheduler
,它使用JobScheduler(最低 API 为 21)。有关应用权限要求,请参阅PlatformScheduler javadocs。 -
WorkManagerScheduler
,它使用WorkManager。
-
getForegroundNotification()
:返回服务在前台运行时显示的通知。您可以使用DownloadNotificationHelper.buildProgressNotification
默认样式创建通知。
最后,需要在AndroidManifest.xml
文件中定义服务:
<service android:name="com.myapp.MyDownloadService"
android:exported="false">
<!-- This is needed for Scheduler -->
<intent-filter>
<action android:name="com.google.android.exoplayer.downloadService.action.RESTART"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</service>
DemoDownloadService
有关具体示例,请参见AndroidManifest.xml
ExoPlayer 演示应用程序。
创建下载管理器
以下代码片段演示了如何实例化 一个 DownloadManager
,可以在 实例化的DownloadService
中的getDownloadManager() 返回。
// 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
,如本页下方所述。以下示例显示了如何创建下载请求:
DownloadRequest downloadRequest =
new DownloadRequest.Builder(contentId, contentUri).build();
其中contentId
是内容的唯一标识符。在一般的情况下, contentUri
通常可以用作contentId
,但是应用程序可以自由使用最适合其用例的任何 ID 方案。DownloadRequest.Builder
还有一些可选的设置器。例如,setKeySetId
和setData
可用于分别设置应用希望与下载关联的 DRM 和自定义数据。也可以使用 指定内容的 MIME 类型setMimeType
,在contentUri
无法推断媒体类型的情况下使用。
创建后,可以将请求发送DownloadService
到添加下载:
DownloadService.sendAddDownload(
context,
MyDownloadService.class,
downloadRequest,
/* foreground= */ false)
其中MyDownloadService
是app的DownloadService
子类, foreground
参数控制服务是否会在前台启动。如果您的应用程序已经在前台,那么该foreground
参数通常应该设置为false
,因为DownloadService
如果它确定它有工作要做,它将把自己置于前台。
删除下载
可以通过向 发送删除命令来删除下载DownloadService
,其中contentId
标识要删除的下载:
DownloadService.sendRemoveDownload(
context,
MyDownloadService.class,
contentId,
/* foreground= */ false)
您还可以使用 删除所有下载的数据 DownloadService.sendRemoveAllDownloads
。
开始和停止下载
满足下面四个条件,下载才会进行:
- 下载没有停止原因。
- 下载不会暂停。
- 满足下载进度的要求。需求可以指定对允许的网络类型的约束,以及设备是否应该空闲或连接到充电器。
- 未超过最大并行下载数。
所有这些条件都可以通过 DownloadService
控制
设置和清除下载停止原因
可以设置一个或所有下载停止的原因:
// 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
。
设置停止原因不会删除下载。部分下载将被保留,清除停止原因将导致下载继续。
当下载有非 STOP_REASON_NONE
原因时,它将处于 Download.STATE_STOPPED
状态。停止原因 DownloadIndex
保留在 中,如果应用程序进程被杀死并稍后重新启动,则保留原因。
暂停和恢复所有下载
所有下载都可以按如下方式暂停和恢复:
// 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
:
// Set the download requirements.
DownloadService.sendSetRequirements(
context,
MyDownloadService.class,
requirements,
/* foreground= */ false);
当由于不满足要求而无法进行下载时,它将处于Download.STATE_QUEUED
状态。您可以使用 查询未满足的要求DownloadManager.getNotMetRequirements()
。
设置最大并行下载数
可以通过调用来设置最大并行下载数 DownloadManager.setMaxParallelDownloads()
。这通常在创建 时完成DownloadManager
,如上例所示。
当由于已达到最大并行下载数而无法继续下载时,它将处于Download.STATE_QUEUED
状态。
查询下载
可以查询所有下载的状态,包括已完成或失败的下载DownloadIndex
。 可以通过调用DownloadManager
获得。然后可以通过调用来获得遍历所有下载的游标 。或者,可以通过调用查询单次下载的状态。DownloadIndex``DownloadManager.getDownloadIndex()``DownloadIndex.getDownloads()``DownloadIndex.getDownload()
DownloadManager
还提供DownloadManager.getCurrentDownloads()
,它只返回当前(即未完成或失败)下载的状态。此方法对于更新通知和其他显示当前下载进度和状态的 UI 组件很有用。
监听下载
您可以添加一个侦听器以DownloadManager
在当前下载更改状态时收到通知:
downloadManager.addListener(
new DownloadManager.Listener() {
// Override methods of interest here.
});
DownloadManagerListener
在演示应用程序的DownloadTracker
类中查看具体示例。
下载进度更新不会触发对DownloadManager.Listener
. 要更新显示下载进度的 UI 组件,您应该DownloadManager
以所需的更新速率定期查询。DownloadService
包含一个示例,它会定期更新服务前台通知。
播放下载的内容
播放下载的内容类似于播放在线内容,不同之处在于数据是从下载中读取的,Cache
而不是通过网络读取的。
不要尝试直接从下载目录中读取文件,这一点很重要。相反,请使用如下所述的 ExoPlayer 库类。
要播放下载的内容,CacheDataSource.Factory
请使用与下载相同的 实例创建一个,并在构建播放器时Cache
将其注入 :DefaultMediaSourceFactory
// 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(cacheDataSourceFactory))
.build();
如果相同的播放器实例也将用于播放未下载的内容,CacheDataSource.Factory
则应将其配置为只读,以避免在播放期间也下载该内容。
一旦播放器配置了CacheDataSource.Factory
,它将可以访问下载的内容进行播放。播放下载就像将对应MediaItem
的文件传递给播放器一样简单。一个MediaItem
可以从Download
获得Download.request.toMediaItem
,也可以直接从DownloadRequest
获得DownloadRequest.toMediaItem
。
媒体源配置
上面的示例使下载缓存可用于所有 MediaItem
的播放。也可以使下载缓存用于单个MediaSource
实例,可以直接传递给播放器:
ProgressiveMediaSource mediaSource =
new ProgressiveMediaSource.Factory(cacheDataSourceFactory)
.createMediaSource(MediaItem.fromUri(contentUri));
player.setMediaSource(mediaSource);
player.prepare();
下载和播放自适应流
自适应流(例如 DASH、SmoothStreaming 和 HLS)通常包含多个媒体轨道。通常有多个轨道包含不同质量的相同内容(例如标清、高清和 4K 视频轨道)。也可能有多个相同类型的轨道包含不同的内容(例如,不同语言的多个音轨)。
对于流式播放,可以使用Track选择器来选择播放哪些Track。类似地,对于下载,DownloadHelper
可以使用 Track选择器来选择下载哪些曲目。DownloadHelper
典型用法遵循以下步骤:
-
DownloadHelper 使用其中一种方法构建一个 DownloadHelper.forMediaItem。 准备DownloadHelper并等待callback。
DownloadHelper downloadHelper = DownloadHelper.forMediaItem( context, MediaItem.fromUri(contentUri), new DefaultRenderersFactory(context), dataSourceFactory); downloadHelper.prepare(myCallback);
或者,使用 和 检查默认选定的轨道, 并使用
getMappedTrackInfo
和getTrackSelections
进行调整。clearTrackSelections``replaceTrackSelections``addTrackSelection
DownloadRequest
通过调用为选定的Track 创建一个getDownloadRequest
。如上所述,可以将请求传递给您DownloadService
以添加下载。使用 释放助手
release()
。
播放下载的自适应内容需要配置播放器并传递相应的MediaItem
,如上所述。
构建时MediaItem
,MediaItem.playbackProperties.streamKeys
必须设置为与 中的匹配,DownloadRequest
以便播放器仅尝试播放已下载的曲目子集。使用 Download.request.toMediaItem
和DownloadRequest.toMediaItem
构建 MediaItem
会为您解决这个问题。