前言
之前做短视频项目,需求是需要视频缓存功能,我也觉得比较合理,毕竟一个视频看完之后重复观看的时候还需要从网上加载是很不友好的事情,一方面耗费用户的流量,另一方面直接从本地播放要更流畅,特别是在seek的时候。在github上看到了AndroidVideoCache,使用起来非常方便,大概知道它是用代理实现的,但是代理具体怎么做的一直没去深究,今天刚好得空,好好研究下,和大家分享。源码链接 https://github.com/danikula/AndroidVideoCache
AndroidVideoCache的用法
1.添加依赖 compile 'com.danikula:videocache:2.7.1'
2.在Application里面创建全局单例 HttpProxyCacheServer,代码如下
public class App extends Application {
private HttpProxyCacheServer proxy;
public static HttpProxyCacheServer getProxy(Context context) {
App app = (App) context.getApplicationContext();
return app.proxy == null ? (app.proxy = app.newProxy()) : app.proxy;
}
private HttpProxyCacheServer newProxy() {
return new HttpProxyCacheServer(this);
}
}
3.在给播放器设置url的时候通过生成代理url来实现视频的缓存,示例代码如下
private void startVideo() {
//拿到全局的单例 HttpProxyCacheServer
HttpProxyCacheServer proxy = App.getProxy(getActivity());
//注册下载缓存监听
proxy.registerCacheListener(this, url);
//生成代理url
String proxyUrl = proxy.getProxyUrl(url);
//给播放器设置播放路径的时候设置为上一步生成的proxyUrl
videoView.setVideoPath(proxyUrl);
videoView.start();
}
以上就是AndroidVideoCache的使用,是不是特简单!当然它还可以设置缓存的大小,缓存路径、缓存文件的数量等,在初始化的时候设置即可,代码如下
private HttpProxyCacheServer newProxy() {
return new HttpProxyCacheServer.Builder(this)
.cacheDirectory(Utils.getVideoCacheDir(this))//缓存路径
.maxCacheFilesCount(100)//最大缓存文件数量
.maxCacheSize(500 * 1024 * 1024)//最大缓存大小
.build();
}
源码分析
分析源码之前,我们先大致了解一下这个框架的工作流程
1.播放器播放视频的时候会使用我们给它设置的代理url,访问proxyUrl的时候自然走到了ProxyServer
2.ProxyServer先判断是否有本地缓存,如果有本地缓存那么直接将本地缓存返回
3.如果没有本地缓存,那么ProxyServer会使用原视频url去远程服务器RemoteServer请求视频数据,获取到RemoteServer返回的数据后再缓存到本地(需要缓存则缓存),然后再通知播放器进度更新同时将数据返回给播放器播放
分析源码当然要找一个入口,这里我们的入口当然是初始化HttpProxyCacheServer的地方,以上面的Builder方式初始化为例,我们看看HttpProxyCacheServer.Builder代码
public Builder(Context context) {
this.sourceInfoStorage = SourceInfoStorageFactory.newSourceInfoStorage(context);
//设置缓存路径
this.cacheRoot = StorageUtils.getIndividualCacheDirectory(context);
//设置缓存策略,采用限制大小的LRU策略
this.diskUsage = new TotalSizeLruDiskUsage(DEFAULT_MAX_SIZE);
//主要用来生成缓存文件名
this.fileNameGenerator = new Md5FileNameGenerator();
//默认不添加Http头信息
this.headerInjector = new EmptyHeadersInjector();
}
这里我们只分析this.sourceInfoStorage,其它的都已备注,没什么好解释的。我们看看SourceInfoStorageFactory
.newSourceInfoStorage做了什么,其实它只是new 了一个DatabaseSourceInfoStorage,源码如下
public class SourceInfoStorageFactory {
public static SourceInfoStorage newSourceInfoStorage(Context context) {
return new DatabaseSourceInfoStorage(context);
}
public static SourceInfoStorage newEmptySourceInfoStorage() {
return new NoSourceInfoStorage();
}
}
通过名字我们可以看出,这个用到了SQLite,我们再跟进去看看DatabaseSourceInfoStorage的构造,就会发现缓存的信息其实是存储在数据库里面的
class DatabaseSourceInfoStorage extends SQLiteOpenHelper implements SourceInfoStorage {
private static final String TABLE = "SourceInfo";
private static final String COLUMN_ID = "_id";
private static final String COLUMN_URL = "url";
private static final String COLUMN_LENGTH = "length";
private static final String COLUMN_MI