ExoPlayer的缓存- 一 使用简介

ExoPlayer 的 缓存-- 一 使用简介

原文链接

ExoPlayer 提供了媒体离线下载功能。在大多数用例中,即使应用程序在后台,也可以继续下载。应用实现这个工呢,应用程序应该继承子类DownloadService 并实例化,并向DownloadService发送命令以添加、删除和控制下载。下图显示了涉及的主要类。

image.png

图 1.下载媒体的类。箭头方向表示数据流向。

  • DownloadService: 包装 一个DownloadManager并将命令转发给它。DownloadManager即使应用程序在后台,该服务也允许继续运行。
  • DownloadManager:管理多个下载,并使用DownloadIndex 加载 存储它们的状态,根据网络连接等要求启动和停止下载等。要下载内容,DownloadManager通常会读取从 HttpDataSource 下载的数据 ,并将其写入Cache.
  • DownloadIndex:保存下载状态。

创建下载服务

要创建DownloadService,需要对其进行子类化并实现其抽象方法:

  • getDownloadManager():返回要使用的DownloadManager

  • getScheduler(): 返回一个可选 Scheduler
    

    当满足挂起下载进度所需的要求时,它可以重新启动服务。ExoPlayer 提供了这些实现:

  • 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.xmlExoPlayer 演示应用程序。

创建下载管理器

以下代码片段演示了如何实例化 一个 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还有一些可选的设置器。例如,setKeySetIdsetData可用于分别设置应用希望与下载关联的 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 典型用法遵循以下步骤:

  1. DownloadHelper 使用其中一种方法构建一个 DownloadHelper.forMediaItem。 准备DownloadHelper并等待callback。

    DownloadHelper downloadHelper =
        DownloadHelper.forMediaItem(
            context,
            MediaItem.fromUri(contentUri),
            new DefaultRenderersFactory(context),
            dataSourceFactory);
    downloadHelper.prepare(myCallback);
    
  2. 或者,使用 和 检查默认选定的轨道, 并使用getMappedTrackInfogetTrackSelections进行调整。clearTrackSelections``replaceTrackSelections``addTrackSelection

  3. DownloadRequest通过调用为选定的Track 创建一个getDownloadRequest。如上所述,可以将请求传递给您DownloadService以添加下载。

  4. 使用 释放助手release()

播放下载的自适应内容需要配置播放器并传递相应的MediaItem,如上所述。

构建时MediaItemMediaItem.playbackProperties.streamKeys必须设置为与 中的匹配,DownloadRequest以便播放器仅尝试播放已下载的曲目子集。使用 Download.request.toMediaItemDownloadRequest.toMediaItem构建 MediaItem会为您解决这个问题。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,948评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,371评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,490评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,521评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,627评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,842评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,997评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,741评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,203评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,534评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,673评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,339评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,955评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,770评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,000评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,394评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,562评论 2 349