当前位置:   article > 正文

ExoPlayer的缓存 四 缓存Cache 的应用_exoplayer defaulthttpdatasource

exoplayer defaulthttpdatasource

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);
  • 1
  • 2

每首歌曲有一个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();
                    }
                });
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

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

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

加密文件的处理

缓存下载 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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

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

CryptoWrapDataSource

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

return new ProgressiveMediaSource.Factory(
        new CryptoWrapDataSource.Factory(buildCacheDataSourceFactory(cache)),
        .createMediaSource(uri);
  • 1
  • 2
  • 3

文件分片大小

默认的分片大小为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
        );
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

下载网速的计算

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

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

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

注册自定义的DownloadManagerListener

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

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);
            }
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

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;
        }
    });
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68

下载参数的设置 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() {

    }
  ......
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

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;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

CacheDownloadParameters

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

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

    public Builder buildUpon(){
        return new Builder(this);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/299241
推荐阅读
相关标签
  

闽ICP备14008679号