ExoPlayer的缓存 四 缓存Cache 的应用

ExoPlayer的缓存-- 四 Cache 的使用

缓存ID

默认的CacheID

同一文件 内容服务器 生成的链接有时效性,如果用URL做缓存ID,会出现同一首无法命中缓存的情况。Exoplayer 的缓存默认使用 URL

public static final CacheKeyFactory DEFAULT_CACHE_KEY_FACTORY =
    (dataSpec) -> dataSpec.key != null ? dataSpec.key : generateKey(dataSpec.uri);

每首歌曲有一个SongID, 如果使用SongID, 每首歌曲的有伴奏 原唱 视频三种文件,也有冲突的可能。

因此使用URL 的path 作为SongID 是比较合适的。

改造后的CacheID

Exoplayer 在创建CacheDataSourceFactory 的时候 传入CacheKeyFactory

        return new CacheDataSourceFactory(
                cache,
                new DefaultHttpDataSourceFactory("user-agent"),
                new FileDataSource.Factory(),
                new CacheDataSinkFactory(cache, CacheDataSink.DEFAULT_FRAGMENT_SIZE),
                CacheDataSource.FLAG_IGNORE_CACHE_ON_ERROR, null,
                new CacheKeyFactory() {
                    @Override
                    public String buildCacheKey(DataSpec dataSpec) {
                        return dataSpec.key != null ? dataSpec.key : dataSpec.uri.getLastPathSegment();
                    }
                });
}

缓存的时候 生成DownloadRequest 直接使用URL 的path 最为ID参数传入

DownloadRequest request = CacheDownloadManager.buildDownloadRequest(uri.getLastPathSegment(), uri);

加密文件的处理

缓存下载 DefaultHttpDataSourceFactory

K歌播放的文件时加密类型的,在缓存的时候 直接使用DefaultHttpDataSourceFactory 下载流。

因此缓存的文件是加密的。

private static synchronized HttpDataSource.Factory getHttpDataSourceFactory( ) {
    if (sHttpDataSourceFactory == null) {
        CookieManager cookieManager = new CookieManager();
        cookieManager.setCookiePolicy(CookiePolicy.ACCEPT_ORIGINAL_SERVER);
        CookieHandler.setDefault(cookieManager);
        sHttpDataSourceFactory = new DefaultHttpDataSourceFactory("user-agent");
    }
    return sHttpDataSourceFactory;
}

在播放的时候需要先解密数据,使用 CryptoHttpDataSourceFactory,但是CryptoHttpDataSourceFactory 使用缓存后保存在缓存中的是解密后的数据,播放中导致数据无法识别。

CryptoWrapDataSource

那能不能在播放的时候 送到播放器的数据解密但是下载保存的时候是原始数据呢。Exoplayer 的DataSource 设计使用了装饰模式,参考这个思路,在CacheDataSource 外面再装饰一层解密的CryptoWrapDataSource。 这样Cache 缓存的时候是加密的数据,送到Exoplayer 的CryptoWrapDataSource 是解密以后的数据。

return new ProgressiveMediaSource.Factory(
        new CryptoWrapDataSource.Factory(buildCacheDataSourceFactory(cache)),
        .createMediaSource(uri);

文件分片大小

默认的分片大小为5M,CacheDataSink.DEFAULT_FRAGMENT_SIZE

在生成DownloadManager 的时候,传入自定义参数,CacheDataSinkFactory 定义 分片大小。

DownloaderConstructorHelper downloaderConstructorHelper = new DownloaderConstructorHelper(
        getDownloadCache(context),
        getHttpDataSourceFactory(),
        null,
        new CacheDataSinkFactory(
                getDownloadCache(context),
                (sParameters != null && sParameters.mSegmentSize > 0) ? sParameters.mSegmentSize:CacheDataSink.DEFAULT_FRAGMENT_SIZE
        ),
        null
);

DefaultDownloaderFactory downloaderFactory = new DefaultDownloaderFactory(downloaderConstructorHelper);

sDownloadManager = new DownloadManager(
                context,
                new DefaultDownloadIndex(getDatabaseProvider(context)),
                downloaderFactory
        );

下载网速的计算

在Exoplayer 的官方文档中 对下载速度的 需要通过DownloadManager 定期查询。DownloadService 支持Notification 通知,默认实现了一个 ForegroundNotificationUpdater 定期通知 Notification。

下载进度更新不会触发对DownloadManager.Listener. 要更新显示下载进度的 UI 组件,您应该DownloadManager以所需的更新速率定期查询。DownloadService 包含一个示例,它会定期更新服务前台通知。

参考这个实现 实现我们的网速监听。

注册自定义的DownloadManagerListener

mDownloadManagerListener = new DownloadManagerListener(mHandlerListener);
CacheDownloadService.getDownloadManager(mContext).addListener(mDownloadManagerListener);

onDownloadChanged 中监听是否有下载开始,如果有下载开始,开始抛出消息计算网速。

private class DownloadManagerListener implements com.google.android.exoplayer2.offline.DownloadManager.Listener {
    private Handler mHandler;

    public DownloadManagerListener(Handler handler){
        mHandler = handler;
    }

    @Override
    public void onInitialized(com.google.android.exoplayer2.offline.DownloadManager downloadManager) {
        mDownloadManager = downloadManager;
        initHandler();
    }

    @Override
    public void onDownloadChanged(com.google.android.exoplayer2.offline.DownloadManager downloadManager, Download download) {
        if(mHandler != null){
            if (download.state == Download.STATE_DOWNLOADING) {
                Message message = Message.obtain();
                message.what = MSG_START;
                message.obj = download;
                if (download.contentLength > 0) {
                    mHandler.sendMessage(message);
                }
            } else if (download.state == Download.STATE_COMPLETED) {
                Message message = Message.obtain();
                message.what = MSG_FINISH;
                message.obj = download;
                mHandler.sendMessage(message);
            }
        }
    }

initHandler() 中 初始化一个线程 用于处理消息和和计算网速

private void initHandler(){
    mHandlerThreadListener = new HandlerThread("DownloadManagerListener");
    mHandlerThreadListener.start();
    mHandlerListener = new Handler(mHandlerThreadListener.getLooper(), new Handler.Callback(){

        @Override
        public boolean handleMessage(@NonNull Message msg) {
            switch (msg.what) {
                case MSG_START: {
                    Download download = (Download) msg.obj;
                    DownloadInfo downloadInfo = mHashDownloadInfo.get(download.request.id);

                    if (downloadInfo == null) {
                        downloadInfo = new DownloadInfo();
                        downloadInfo.id = download.request.id;
                        downloadInfo.updateTimeMs = download.startTimeMs;
                        downloadInfo.bytesDownloaded = download.getBytesDownloaded();
                        mHashDownloadInfo.put(downloadInfo.id, downloadInfo);
                    }

                    if (!mRunning) {
                        mRunning = true;
                        mHandlerListener.sendEmptyMessage(MSG_UPDATE);
                    }
                }
                break;
                case MSG_UPDATE: {
                    List<Download> downloadList = mDownloadManager.getCurrentDownloads();
                    if (downloadList != null && downloadList.size() > 0) {
                        long nowMs = Clock.DEFAULT.elapsedRealtime();
                        for (Download dl : downloadList) {

                            DownloadInfo downloadInfo = mHashDownloadInfo.get(dl.request.id);
                            long speed = (dl.getBytesDownloaded() - downloadInfo.bytesDownloaded)
                                    / ((nowMs - downloadInfo.updateTimeMs) / 1000);

                            downloadInfo.updateTimeMs = nowMs;
                            downloadInfo.bytesDownloaded = dl.getBytesDownloaded();

                            for (NetworkSpeedListener li : sListener) {
                                li.onProgress(dl, speed, dl.getPercentDownloaded());
                            }
                        }
                        mHandlerListener.sendEmptyMessageDelayed(MSG_UPDATE, 1000);
                    } else {
                        mRunning = false;
                    }
                }
                break;
                case MSG_FINISH:
                    Download download = (Download) msg.obj;
                    DownloadInfo downloadInfo = mHashDownloadInfo.get(download.request.id);
                    long nowMs = Clock.DEFAULT.elapsedRealtime();

                    long speed = (download.getBytesDownloaded() - downloadInfo.bytesDownloaded )
                            / (nowMs - downloadInfo.updateTimeMs);

                    mHashDownloadInfo.remove(((Download) msg.obj).request.id);

                    for (NetworkSpeedListener li : sListener) {
                        li.onProgress((Download) msg.obj, speed, download.getPercentDownloaded());
                    }
                    break;
            }
            return false;
        }
    });
}

下载参数的设置 Builder 设计模式 的使用

下载参数的设置使用了 Builder 设计模式

在这里插入图片描述

定义基类 ParametersBase

  1. 基类中定义 各种属性
  2. 基类中定义 setXXX
  3. 默认构造函数ParametersBase(ParametersBase base) 这样变量的 赋值 操作都放在 基类中操作,做到统一
class ParametersBase {
    protected long mMaxCacheSize = 512 * 1025 * 1025;
    protected long mSegmentSize = mMaxCacheSize;

    protected String mCachePath;
    protected int mMaxParallelDownloads;

    ParametersBase(ParametersBase base){
        this.mMaxCacheSize = base.mMaxCacheSize;
        this.mSegmentSize = base.mSegmentSize;
        this.mCachePath = base.mCachePath;
        this.mMaxParallelDownloads = base.mMaxParallelDownloads;
    }

    ParametersBase() {

    }
  ......
}

Builder 类

  1. Builder 类中增加 setXXX 函数
  2. build 函数。 build 函数中 直接使用基类构造函数 生成 CacheDownloadParameters
public static class Builder extends ParametersBase{
    public Builder(){
        super();
    }
    public Builder(ParametersBase base) {
        super(base);
    }
    ......
    public CacheDownloadParameters build(){
        CacheDownloadParameters parameters =  new CacheDownloadParameters(this);
        return parameters;
    }

CacheDownloadParameters

  1. 继承基类构造函数
  2. buildUpon 函数 使用 基类构造函数 生成
public class CacheDownloadParameters extends ParametersBase {

    private CacheDownloadParameters(ParametersBase base) {
        super(base);
    }

    public Builder buildUpon(){
        return new Builder(this);
    }
}
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值