系列文章目录
ExoPlayer架构详解与源码分析(1)——前言
ExoPlayer架构详解与源码分析(2)——Player
ExoPlayer架构详解与源码分析(3)——Timeline
ExoPlayer架构详解与源码分析(4)——整体架构
ExoPlayer架构详解与源码分析(5)——MediaSource
ExoPlayer架构详解与源码分析(6)——MediaPeriod
ExoPlayer架构详解与源码分析(7)——SampleQueue
ExoPlayer架构详解与源码分析(8)——Loader
ExoPlayer架构详解与源码分析(9)——TsExtractor
ExoPlayer架构详解与源码分析(10)——H264Reader
ExoPlayer架构详解与源码分析(11)——DataSource
ExoPlayer架构详解与源码分析(12)——Cache
ExoPlayer架构详解与源码分析(13)——TeeDataSource和CacheDataSource
前言
书接上回,继续贴下上文的蜘蛛网
铺垫了那么多的基础,本篇终于可以来分析下CacheDataSource,上篇重点讲完了图的Cache的下半部分,而将Cache和CacheDataSource关联起来的是一个叫TeeDataSource特殊DataSource。
TeeDataSource
这是一个3通阀门一样的DataSource,有一个输入端2个输出端,也可以理解为在正常的输入和输出端中间又接了一个输出端,类似于下图
upstream也是一个DataSource,一般是上游的原始数据源,如OkHttpDataSource获取的网络源,dataSink参照上篇ExoPlayer架构详解与源码分析(12)——Cache,这些参数在TeeDataSource初始化时设置的。
看下代码实现
@Override
//打开数据源
public long open(DataSpec dataSpec) throws IOException {
bytesRemaining = upstream.open(dataSpec);//打开上游数据源
if (bytesRemaining == 0) {
return 0;
}
if (dataSpec.length == C.LENGTH_UNSET && bytesRemaining != C.LENGTH_UNSET) {
// 根据实际长度裁剪dataSpec,保证传给dataSink长度正确
dataSpec = dataSpec.subrange(0, bytesRemaining);
}
dataSinkNeedsClosing = true;
dataSink.open(dataSpec);//dataSink打开流,主要就是通过cache startFile打开缓存文件获取到流
return bytesRemaining;
}
@Override
//读取数据
@Override
public int read(byte[] buffer, int offset, int length) throws IOException {
if (bytesRemaining == 0) {
return C.RESULT_END_OF_INPUT;
}
//从上游读取数据
int bytesRead = upstream.read(buffer, offset, length);
if (bytesRead > 0) {
// 在数据返回前,先通过dataSink将数据写入到上面打开的缓存文件流里
dataSink.write(buffer, offset, bytesRead);
if (bytesRemaining != C.LENGTH_UNSET) {
bytesRemaining -= bytesRead;
}
}
//输出数据
return bytesRead;
}
可以看到TeeDataSource实现很简单,TeeDataSource就是为了数据边读边缓存而实现的,只要是读取过的数据都会缓存到文件里,至于在什么时候使用TeeDataSource,这就要讲到本篇的重点CacheDataSource了。
CacheDataSource
读取和写入Cache的DataSource 。如果可能的话,请求会从缓存中获取。当数据未缓存时,会从上游DataSource请求数据并将其写入缓存。
CacheDataSource,主要包含3个DataSource
- upstreamDataSource 上游原始数据的源,如果此时播放的是一个网络的URL文件,这个upstreamDataSource可能就是OkHttpDataSource,无需缓存或者当前已经在缓存时,就会使用此源。
- cacheWriteDataSource 缓存写入源,默认的是TeeDataSource,通过它在数据读取时缓存到文件,当前资源未缓存且需要缓存时,就会使用此源。
- cacheReadDataSource 缓存数据读取源,当前播放的数据已经被缓存时,就会通过这个源将缓存的数据读取出来,正常数据都是缓存在本地文件中的所以这里一般都是FileDataSource,当前资源已经查询到缓存时,就会使用此源。
上面几个源的关系可以在源码的openNextSource方法中看出来。
接下来看源码实现:
private CacheDataSource(
Cache cache,
@Nullable DataSource upstreamDataSource,
DataSource cacheReadDataSource,
@Nullable DataSink cacheWriteDataSink,
@Nullable CacheKeyFactory cacheKeyFactory,
@Flags int flags,
@Nullable PriorityTaskManager upstreamPriorityTaskManager,
int upstreamPriority,
@Nullable EventListener eventListener) {
this.cache = cache;
//用于读取缓存的DataSource,只要是缓存在文件里这里一般都是FileDataSource
this.cacheReadDataSource = cacheReadDataSource;
//cacheKeyFactory 用于生成CacheContent的资源key,默认就是取DataSpec.key,没有则获取播放文件的URL
this.cacheKeyFactory = cacheKeyFactory != null ? cacheKeyFactory : CacheKeyFactory.DEFAULT;
//是否在缓存时阻塞
this.blockOnCache =