Exoplayer的缓存 – 二 下载服务 DownloadService

文章目录

  • DownloadService
    • send 指令
    • onStartCommand 解析Intent
  • DownloadManager
    • DownloadManager 初始化
    • addDownload 的实现
    • **InternalHandler** 的**handleMessage** 方法
  • Download
    • addDownload
    • syncTasks
  • DownloadManager.Task
    • syncQueuedDownload
    • Task.run
  • ProgressiveDownloader
    • ProgressiveDownloader 初始化CacheWriter
    • download cache
  • CacheWrite
  • DownloadIndex

DownloadService

send 指令

DownloadService 是一个android 标准的service, DownloadService 封装了下载的多个指令, 由一系列静态函数组成:

  1. sendAddDownload
  2. sendRemoveDownload
  3. sendRemoveAllDownloads
  4. sendRemoveAllDownloads
  5. sendResumeDownloads
  6. sendPauseDownloads

DownloadService 在发送这些指令时,先把这些指令参数构建一个Intent, 然后通过startService 的方法发送Intent 到 Service.

/*** Starts the service if not started already and adds a new download.** @param context A {@link Context}.* @param clazz The concrete download service to be started.* @param downloadRequest The request to be executed.* @param foreground Whether the service is started in the foreground.*/
public static void sendAddDownload(Context context,Class<? extends DownloadService> clazz,DownloadRequest downloadRequest,boolean foreground) {Intent intent = buildAddDownloadIntent(context, clazz, downloadRequest, foreground);startService(context, intent, foreground);
}

在Service 的 onStartCommand(@Nullable Intent intent, int flags, int startId) 中 解析Intent, 并从Intent 中获取 下载的参数,传递到 DownloadManager

onStartCommand 解析Intent

public int onStartCommand(@Nullable Intent intent, int flags, int startId) {if (intent != null) {intentAction = intent.getAction();contentId = intent.getStringExtra(KEY_CONTENT_ID);...}DownloadManager downloadManager =Assertions.checkNotNull(downloadManagerHelper).downloadManager;switch (intentAction) {...case ACTION_ADD_DOWNLOAD:@NullableDownloadRequest downloadRequest =Assertions.checkNotNull(intent).getParcelableExtra(KEY_DOWNLOAD_REQUEST);if (downloadRequest == null) {Log.e(TAG, "Ignored ADD_DOWNLOAD: Missing " + KEY_DOWNLOAD_REQUEST + " extra");} else {...downloadManager.addDownload(downloadRequest, stopReason);}break;...default:Log.e(TAG, "Ignored unrecognized action: " + intentAction);break;}return START_STICKY;
}

DownloadManager

DownloadManager 实现真正的下载管理工作。

DownloadManager 初始化的时候传入两个参数

  1. WritableDownloadIndex downloadIndex 保存下载信息的数据库 Exoplayer 默认实现 DefaultDownloadIndex(databaseProvider)
  2. DownloaderFactory downloaderFactory 生成下载器的工厂 Exoplayer 默认实现 DefaultDownloaderFactory(new DownloaderConstructorHelper(cache, upstreamFactory)));

DownloadManager 构造函数初始化的时候 初始化了 两个变量 internalThreadinternalHandler

用于内部的消息转发

DownloadManager 初始化

public DownloadManager(Context context, WritableDownloadIndex downloadIndex, DownloaderFactory downloaderFactory) {this.context = context.getApplicationContext();this.downloadIndex = downloadIndex;HandlerThread internalThread = new HandlerThread("ExoPlayer:DownloadManager");internalThread.start();internalHandler =new InternalHandler(internalThread,downloadIndex,downloaderFactory,mainHandler,maxParallelDownloads,minRetryCount,downloadsPaused);internalHandler.obtainMessage(MSG_INITIALIZE, notMetRequirements, /* unused */ 0).sendToTarget();
}

addDownload 的实现

addDownload 内部通过internalHandler 发送消息到internalThread 线程进行消息处理

/*** Adds a download defined by the given request.** @param request The download request.*/
public void addDownload(DownloadRequest request) {addDownload(request, STOP_REASON_NONE);
}public void addDownload(DownloadRequest request, int stopReason) {pendingMessages++;internalHandler.obtainMessage(MSG_ADD_DOWNLOAD, stopReason, /* unused */ 0, request).sendToTarget();
}

InternalHandlerhandleMessage 方法

public void handleMessage(Message message) {boolean processedExternalMessage = true;switch (message.what) {case MSG_INITIALIZE:int notMetRequirements = message.arg1;initialize(notMetRequirements);break;......case MSG_ADD_DOWNLOAD:DownloadRequest request = (DownloadRequest) message.obj;stopReason = message.arg1;addDownload(request, stopReason);break;

Download

Download 用于记录下载相关的信息。下载状态,时间, 下载进度等。

public Download(DownloadRequest request,@State int state,long startTimeMs,long updateTimeMs,long contentLength,int stopReason,@FailureReason int failureReason,DownloadProgress progress) {this.request = request;this.state = state;this.startTimeMs = startTimeMs;this.updateTimeMs = updateTimeMs;this.contentLength = contentLength;this.stopReason = stopReason;this.failureReason = failureReason;this.progress = progress;
}

addDownload

最终 在 private void addDownload(DownloadRequest request, int stopReason) 中 把下载的DownloadRequest 转化为Download, 通过putDownload 把Download 放入到下载队列中。在构建Download 首先检查下是否有正在下载的Download, 如果有 mergeRequest 两个下载请求合并

private void addDownload(DownloadRequest request, int stopReason) {@Nullable Download download = getDownload(request.id, /* loadFromIndex= */ true);long nowMs = System.currentTimeMillis();if (download != null) {putDownload(mergeRequest(download, request, stopReason, nowMs));} else {putDownload(new Download(request,stopReason != STOP_REASON_NONE ? STATE_STOPPED : STATE_QUEUED,/* startTimeMs= */ nowMs,/* updateTimeMs= */ nowMs,/* contentLength= */ C.LENGTH_UNSET,stopReason,FAILURE_REASON_NONE));}syncTasks();
}

syncTasks

syncTasks 函数会根据当前下载队列的状态去同步下载任务

DownloadManager.Task

DownloadManager.Task Task 继承于Thread, 下载线程。

syncQueuedDownload

syncTasks 函数中根据下载状态 调用到syncQueuedDownload 。

首先构建一个 downloaderFactory 创建一个Downloader, 这里的Downloader 实例为ProgressiveDownloader,

然后构建一个Task , 然后Task 启动 通过downloader 下载

private Task syncQueuedDownload(@Nullable Task activeTask, Download download) {Downloader downloader = downloaderFactory.createDownloader(download.request);activeTask =new Task(download.request,downloader,download.progress,/* isRemove= */ false,minRetryCount,/* internalHandler= */ this);activeTasks.put(download.request.id, activeTask);if (activeDownloadTaskCount++ == 0) {sendEmptyMessageDelayed(MSG_UPDATE_PROGRESS, UPDATE_PROGRESS_INTERVAL_MS);}activeTask.start();return activeTask;
}

Task.run

Task 的run 函数

@Override
public void run() {try {if (isRemove) {} else {while (!isCanceled) {try {downloader.download(/* progressListener= */ this);break;} catch (IOException e) {...}}}}} catch (InterruptedException e) {Thread.currentThread().interrupt();}@Nullable Handler internalHandler = this.internalHandler;if (internalHandler != null) {internalHandler.obtainMessage(MSG_TASK_STOPPED, this).sendToTarget();}
}

ProgressiveDownloader

ProgressiveDownloader 初始化CacheWriter

ProgressiveDownloader的构造函数中 通过MediaItem CacheDataSource.Factory 生成 dataSource

然后 new 出 CacheWriter

public ProgressiveDownloader(MediaItem mediaItem, CacheDataSource.Factory cacheDataSourceFactory, Executor executor) {dataSpec =new DataSpec.Builder().setUri(mediaItem.localConfiguration.uri).setKey(mediaItem.localConfiguration.customCacheKey).setFlags(DataSpec.FLAG_ALLOW_CACHE_FRAGMENTATION).build();dataSource = cacheDataSourceFactory.createDataSourceForDownloading();@SuppressWarnings("nullness:methodref.receiver.bound")CacheWriter.ProgressListener progressListener = this::onProgress;cacheWriter =new CacheWriter(dataSource, dataSpec, /* temporaryBuffer= */ null, progressListener);
}

download cache

在download 函数中 cacheWriter 构造出一个Runable, 然后post 到 executor 中执行

public void download(@Nullable ProgressListener progressListener)throws IOException, InterruptedException {this.progressListener = progressListener;downloadRunnable =new RunnableFutureTask<Void, IOException>() {@Overrideprotected Void doWork() throws IOException {cacheWriter.cache();return null;}@Overrideprotected void cancelWork() {cacheWriter.cancel();}};try {boolean finished = false;while (!finished && !isCanceled) {if (priorityTaskManager != null) {priorityTaskManager.proceed(C.PRIORITY_DOWNLOAD);}executor.execute(downloadRunnable);try {downloadRunnable.get();} }} finally {}
}

CacheWrite

CacheWrite 中首先 从cache 中获取已下载的长度,

public void cache() throws IOException {bytesCached = cache.getCachedBytes(cacheKey, dataSpec.position, dataSpec.length);if (dataSpec.length != C.LENGTH_UNSET) {endPosition = dataSpec.position + dataSpec.length;} else {long contentLength = ContentMetadata.getContentLength(cache.getContentMetadata(cacheKey));endPosition = contentLength == C.LENGTH_UNSET ? C.POSITION_UNSET : contentLength;}while (endPosition == C.POSITION_UNSET || nextPosition < endPosition) {if (blockLength > 0) {} else {// There's a hole of length -blockLength.blockLength = -blockLength;long nextRequestLength = blockLength == Long.MAX_VALUE ? C.LENGTH_UNSET : blockLength;nextPosition += readBlockToCache(nextPosition, nextRequestLength);}}
}

readBlockToCache 函数中通过 dataSource 读取 数据

private long readBlockToCache(long position, long length) throws IOException {boolean isLastBlock = position + length == endPosition || length == C.LENGTH_UNSET;long resolvedLength = C.LENGTH_UNSET;boolean isDataSourceOpen = false;if (length != C.LENGTH_UNSET) {// If the length is specified, try to open the data source with a bounded request to avoid// the underlying network stack requesting more data than required.DataSpec boundedDataSpec =dataSpec.buildUpon().setPosition(position).setLength(length).build();try {resolvedLength = dataSource.open(boundedDataSpec);} catch (IOException e) {}}int totalBytesRead = 0;try {int bytesRead = 0;while (bytesRead != C.RESULT_END_OF_INPUT) {throwIfCanceled();bytesRead = dataSource.read(temporaryBuffer, /* offset= */ 0, temporaryBuffer.length);if (bytesRead != C.RESULT_END_OF_INPUT) {onNewBytesCached(bytesRead);totalBytesRead += bytesRead;}}if (isLastBlock) {onRequestEndPosition(position + totalBytesRead);}} catch (IOException e) {DataSourceUtil.closeQuietly(dataSource);throw e;}return totalBytesRead;
}

DownloadIndex

DownloadIndex 用于保存Download 的状态。在DownloadManager 中同步

DownloadIndex 的实例类型为 DefaultDownloadIndex, 底层的实现是 SQLiteDatabase。通过DownloaderManger 注入DatabaseProvider

exoplayer 提供了一个默认实现 StandaloneDatabaseProvider

Exoplayer的缓存 二 下载服务DownloadService相关推荐

  1. Exoplayer的缓存 一 使用简介

    Exoplayer 的 缓存-- 一 使用简介 文章目录 创建下载服务 创建下载管理器 添加下载 删除下载 开始和停止下载 设置和清除下载停止原因 暂停和恢复所有下载 设置下载进度要求 设置最大并行下 ...

  2. 《深入理解 Spring Cloud 与微服务构建》第十二章 服务注册和发现 Consul

    <深入理解 Spring Cloud 与微服务构建>第十二章 服务注册和发现 Consul 文章目录 <深入理解 Spring Cloud 与微服务构建>第十二章 服务注册和发 ...

  3. twisted入门教程之五:由Twited支持的诗歌下载服务客户端

    第五部分:由Twited支持的诗歌下载服务客户端 抽象地构建客户端 在第四部分中,我们构建了第一个使用Twisted的客户端.它确实能很好地工作,但仍有提高的空间. 首先是,这个客户端竟然有创建网络端 ...

  4. 【SAP PO】SAP PO 接口配置完整教程之二REST服务对接

    SAP PO 接口配置完整教程之二REST服务对接 1.了解服务协议 1.1.服务通讯协议 1.2.具体接口协议 1.3.接口服务测试 2.PO端接口配置 2.1.PO端ESR配置 2.2.PO端IB ...

  5. 地图缓存和动态地图服务

    参考:https://pan.baidu.com/s/19mw5XmLtJe2SIYe-hX0rgA 地图缓存就是对服务进行预处理,提前在设计好的比例级别下把地图切割成小块的地图切片,也叫瓦片(Til ...

  6. [ Azure | Az-900 ] 基础知识点总结(二) - 核心组件服务

    本系列文章主要针对微软AZ-900所有知识点总结,助力通过考试,获得证书.本系列文章列表如下: [ Azure | Az-900 ] 基础知识点总结(一) - Cloud云概念 [ Azure | A ...

  7. ExoPlayer的缓存 三 SimpleCache的使用

    ExoPlayer的缓存 – 三 Cache的使用 文章目录 CacheDataSource 读取数据 创建 CacheDataSource TeeDataSource 写入缓存数据 CacheDat ...

  8. [缓存]迅雷下载的原理——P2SP加速技术

    BT的出现使大多数人现在对P2P并不陌生,P2P的下载概念,简单点说,就是下载不再象传统方式那样只能依赖服务器,内容的传递可以在网络上的各个终端机器中进行. 而现在,P2SP出现使用户有了更好的选择, ...

  9. 二. 微服务的高级进阶

    二. 微服务的高级进阶 1. Ribbon API和负载均衡算法 1. Ribbon API Ribbon 是一个独立的组件,是用来进行远程接口调用的,代码如下 @Slf4j @Service @Sc ...

最新文章

  1. Sql Injection脚本注入终极利用方法
  2. Good Luck!_JAVA
  3. js基础语法(01)-JS中+号的三种含义
  4. Django从理论到实战(part23)--模板继承
  5. anchor译中文_anchor的意思在线翻译,解释anchor中文英文含义,短语词组,音标读音,例句,词源,同义词【澳典网ODict.Net】...
  6. Vue3里的setup中使用vuex
  7. 阿里云对象存储OSS与文件存储NAS的区别
  8. javascript中的this讲解
  9. 面向对象(Python):学习笔记之封装
  10. Python 文件读写小结
  11. 查看linux系统版本命令大全
  12. URL转换成IP的过程
  13. 楚留香服务器维护时间,【9月28日维护公告】
  14. 为啥互联网都使用缓存
  15. 吃饭的时候吃饭,睡觉的时候睡觉。 (转)
  16. php+仿微信公众号样式,仿微信公众号富文本编辑器
  17. 七周七并发模型与七周七语言
  18. 10年网安经验分享:一般人别瞎入网络安全行业
  19. 【力扣时间】【825】【中等】适龄的朋友
  20. 19.3 Table 1-2.S3C2440A 289-Pin FBGA Pin Assignments (Sheet 4 of 9) (Continued)

热门文章

  1. 金额转换为中文大写格式
  2. 偷偷赚钱的副业(真实有效)
  3. 水利水电安全员考试多选练习题库(8)
  4. 无栈非递归中序遍历非线索化二叉树
  5. 抓起整个网站离线浏览的软件Teleport Pro
  6. BI 到底是什么,看看这篇文章怎么说
  7. pstack 安装linux_Linux下pstack的实现
  8. linux 强制重启 sysrq,用Magic SysRq键实现Linux安全重启
  9. 利用摄像头拍照并保存
  10. linux系统克隆后eth0不见了(IP地址没有了)