Exoplayer的缓存 二 下载服务DownloadService
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 封装了下载的多个指令, 由一系列静态函数组成:
- sendAddDownload
- sendRemoveDownload
- sendRemoveAllDownloads
- sendRemoveAllDownloads
- sendResumeDownloads
- 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 初始化的时候传入两个参数
- WritableDownloadIndex downloadIndex 保存下载信息的数据库 Exoplayer 默认实现 DefaultDownloadIndex(databaseProvider)
- DownloaderFactory downloaderFactory 生成下载器的工厂 Exoplayer 默认实现 DefaultDownloaderFactory(new DownloaderConstructorHelper(cache, upstreamFactory)));
DownloadManager 构造函数初始化的时候 初始化了 两个变量 internalThread 和 internalHandler
用于内部的消息转发
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();
}
InternalHandler 的handleMessage 方法
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相关推荐
- Exoplayer的缓存 一 使用简介
Exoplayer 的 缓存-- 一 使用简介 文章目录 创建下载服务 创建下载管理器 添加下载 删除下载 开始和停止下载 设置和清除下载停止原因 暂停和恢复所有下载 设置下载进度要求 设置最大并行下 ...
- 《深入理解 Spring Cloud 与微服务构建》第十二章 服务注册和发现 Consul
<深入理解 Spring Cloud 与微服务构建>第十二章 服务注册和发现 Consul 文章目录 <深入理解 Spring Cloud 与微服务构建>第十二章 服务注册和发 ...
- twisted入门教程之五:由Twited支持的诗歌下载服务客户端
第五部分:由Twited支持的诗歌下载服务客户端 抽象地构建客户端 在第四部分中,我们构建了第一个使用Twisted的客户端.它确实能很好地工作,但仍有提高的空间. 首先是,这个客户端竟然有创建网络端 ...
- 【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 ...
- 地图缓存和动态地图服务
参考:https://pan.baidu.com/s/19mw5XmLtJe2SIYe-hX0rgA 地图缓存就是对服务进行预处理,提前在设计好的比例级别下把地图切割成小块的地图切片,也叫瓦片(Til ...
- [ Azure | Az-900 ] 基础知识点总结(二) - 核心组件服务
本系列文章主要针对微软AZ-900所有知识点总结,助力通过考试,获得证书.本系列文章列表如下: [ Azure | Az-900 ] 基础知识点总结(一) - Cloud云概念 [ Azure | A ...
- ExoPlayer的缓存 三 SimpleCache的使用
ExoPlayer的缓存 – 三 Cache的使用 文章目录 CacheDataSource 读取数据 创建 CacheDataSource TeeDataSource 写入缓存数据 CacheDat ...
- [缓存]迅雷下载的原理——P2SP加速技术
BT的出现使大多数人现在对P2P并不陌生,P2P的下载概念,简单点说,就是下载不再象传统方式那样只能依赖服务器,内容的传递可以在网络上的各个终端机器中进行. 而现在,P2SP出现使用户有了更好的选择, ...
- 二. 微服务的高级进阶
二. 微服务的高级进阶 1. Ribbon API和负载均衡算法 1. Ribbon API Ribbon 是一个独立的组件,是用来进行远程接口调用的,代码如下 @Slf4j @Service @Sc ...
最新文章
- Sql Injection脚本注入终极利用方法
- Good Luck!_JAVA
- js基础语法(01)-JS中+号的三种含义
- Django从理论到实战(part23)--模板继承
- anchor译中文_anchor的意思在线翻译,解释anchor中文英文含义,短语词组,音标读音,例句,词源,同义词【澳典网ODict.Net】...
- Vue3里的setup中使用vuex
- 阿里云对象存储OSS与文件存储NAS的区别
- javascript中的this讲解
- 面向对象(Python):学习笔记之封装
- Python 文件读写小结
- 查看linux系统版本命令大全
- URL转换成IP的过程
- 楚留香服务器维护时间,【9月28日维护公告】
- 为啥互联网都使用缓存
- 吃饭的时候吃饭,睡觉的时候睡觉。 (转)
- php+仿微信公众号样式,仿微信公众号富文本编辑器
- 七周七并发模型与七周七语言
- 10年网安经验分享:一般人别瞎入网络安全行业
- 【力扣时间】【825】【中等】适龄的朋友
- 19.3 Table 1-2.S3C2440A 289-Pin FBGA Pin Assignments (Sheet 4 of 9) (Continued)