Android技术研究安卓常用框架及依赖库资料 | 汇总

优雅的构建 Android 项目之磁盘缓存(DiskLruCa

2017-02-10  本文已影响426人  PandaQ404

Android 的缓存技术

一个优秀的应用首先它的用户体验是优秀的,在 Android 应用中恰当的使用缓存技术不仅可以缓解服务器压力还可以优化用户的使用体验,减少用户流量的使用。在 Android 中缓存分为内存缓存和磁盘缓存两种:

内存缓存

磁盘缓存

DiskLruCache

DiskLruCache 是 JakeWharton 大神在 github 上的一个开源库,代码量并不多。与谷歌官方的内存缓存策略LruCache 相对应,DiskLruCache 也遵从于 LRU(Least recently used 最近最少使用)算法,只不过存储位置在磁盘上。虽然在谷歌的文档中有提到但 DiskLruCache 并未集成到官方的 API中,使用的话按照 github 库中的方式集成就行。
DiskLruCache 使用时需要注意:

使用方法

打开缓存

DiskLruCache 不能使用 new 的方式创建,创建一个缓存对象方式如下:

/**
*参数说明
*
*cacheFile 缓存文件的存储路径
*appVersion 应用版本号。DiskLruCache 认为应用版本更新后所有的数据都因该从服务器重新拉取,因此需要版本号进行判断
*1 每条缓存条目对应的值的个数,这里设置为1个。
*Constants.CACHE_MAXSIZE 我自己定义的常量类中的值表示换粗的最大存储空间
**/
DiskLruCache mDiskLruCache = DiskLruCache.open(cacheFile, appVersion, 1, Constants.CACHE_MAXSIZE);

存入缓存

DiskLruCache 存缓存是通过 DiskLruCache.Editor 处理的:

/**
 *此处是为代码,实际使用还需要 try catch 处理可能出现的异常
 *
 **/
String key = getMD5Result(key);
DiskLruCache.Editor editor = mDiskLruCache.edit(key);
OutputStream os = editor.newOutputStream(0);
//此处存的一个 新闻对象因此用 ObjectOutputStream
ObjectOutputStream outputStream = new ObjectOutputStream(os);
outputStream.writeObject(stories);
//别忘了关闭流和提交编辑
outputStream.close();
editor.commit();

取出缓存

DiskLruCache 取缓存是通过 DiskLruCache.Snapshot 处理的:

/**
 *此处是为代码,实际使用还需要 try catch 处理可能出现的异常
 *
 **/
String key = getMD5Result(key);
//通过设置的 key 去获取缩略对象
DiskLruCache.Snapshot snapshot = mDiskLruCache.get(key);
//通过 SnapShot 对象获取流数据
InputStream in = snapshot.getInputStream(0);
ObjectInputStream ois = new ObjectInputStream(in);
//将流数据转换为 Object 对象
ArrayList<ZhihuStory> stories = (ArrayList<ZhihuStory>) ois.readObject();

使用 DiskLruCache 进行磁盘缓存基本流程就这样,开——>存 或者 开——>取。

完整流程的代码

    //使用rxandroid+retrofit进行请求
    public void loadDataByRxandroidRetrofit() {
        mINewsListActivity.showProgressBar();
        Subscription subscription = ApiManager.getInstence().getDataService()
                .getZhihuDaily()
                .map(new Func1<ZhiHuDaily, ArrayList<ZhihuStory>>() {
                    @Override
                    public ArrayList<ZhihuStory> call(ZhiHuDaily zhiHuDaily) {
                        ArrayList<ZhihuStory> stories = zhiHuDaily.getStories();
                        if (stories != null) {
                            //加载成功后将数据缓存倒本地(demo 中只有一页,实际使用时根据需求选择是否进行缓存)
                            makeCache(zhiHuDaily.getStories());
                        }
                        return stories;
                    }
                })
                //设置事件触发在非主线程
                .subscribeOn(Schedulers.io())
                //设置事件接受在UI线程以达到UI显示的目的
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Subscriber<ArrayList<ZhihuStory>>() {
                    @Override
                    public void onCompleted() {
                        mINewsListActivity.hidProgressBar();
                    }

                    @Override
                    public void onError(Throwable e) {
                        mINewsListActivity.getDataFail("", e.getMessage());
                    }

                    @Override
                    public void onNext(ArrayList<ZhihuStory> stories) {
                        mINewsListActivity.getDataSuccess(stories);
                    }
                });
        //绑定观察对象,注意在界面的ondestory或者onpouse方法中调用presenter.unsubcription();
        addSubscription(subscription);
    }
    
    //生成Cache
    private void makeCache(ArrayList<ZhihuStory> stories) {
        File cacheFile = getCacheFile(MyApplication.getContext(), Constants.ZHIHUCACHE);
        DiskLruCache diskLruCache = DiskLruCache.open(cacheFile, MyApplication.getAppVersion(), 1, Constants.CACHE_MAXSIZE);
        try {
            //使用MD5加密后的字符串作为key,避免key中有非法字符
            String key = SecretUtil.getMD5Result(Constants.ZHIHUSTORY_KEY);
            DiskLruCache.Editor editor = diskLruCache.edit(key);
            if (editor != null) {
                ObjectOutputStream outputStream = new ObjectOutputStream(editor.newOutputStream(0));
                outputStream.writeObject(stories);
                outputStream.close();
                editor.commit();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    //加载Cache
    public void loadCache() {
        File cacheFile = getCacheFile(MyApplication.getContext(), Constants.ZHIHUCACHE);
        DiskLruCache diskLruCache = DiskLruCache.open(cacheFile, MyApplication.getAppVersion(), 1, Constants.CACHE_MAXSIZE);
        String key = SecretUtil.getMD5Result(Constants.ZHIHUSTORY_KEY);
        try {
            DiskLruCache.Snapshot snapshot = diskLruCache.get(key);
            if (snapshot != null) {
                InputStream in = snapshot.getInputStream(0);
                ObjectInputStream ois = new ObjectInputStream(in);
                try {
                    ArrayList<ZhihuStory> stories = (ArrayList<ZhihuStory>) ois.readObject();
                    if (stories != null) {
                        mINewsListActivity.getDataSuccess(stories);
                    } else {
                        mINewsListActivity.getDataFail("", "无数据");
                    }
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    //获取Cache 存储目录
    private  File getCacheFile(Context context, String uniqueName) {
        String cachePath = null;
        if ((Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())
                || !Environment.isExternalStorageRemovable())
                && context.getExternalCacheDir() != null) {
            cachePath = context.getExternalCacheDir().getPath();
        } else {
            cachePath = context.getCacheDir().getPath();
        }
        return new File(cachePath + File.separator + uniqueName);
    }

上面的代码跑通流程存 Cache 取 Cache 是没有问题的,但是这么写肯定是不优雅的!两年前的我可能会将这样的代码作为发布代码。

方法封装,优雅的使用

既有 key 又有 value 还有 Editor 的你想到了什么?应该是 SharePreferences 吧!在 MVPDemo 中我构建了一个 DiskLruCacheManager 类来封装 Cache 的存取。代码就不贴了,大家自行在 demo 中查看 DiskManager 类,我只说一下怎么使用它来存取 Cache:

存取都一样需要先拿到 DiskManager 的实例

DiskCacheManager manager = new DiskCacheManager(MyApplication.getContext(), Constants.ZHIHUCACHE);

然后通过 manager 的公共方法进行数据的存取:

数据类型 存入方法 取出方法 说明
String put(String key,String value) getString(String key) 返回String对象
JsonObject put(String key,JsonObject value) getJsonObject(String key) 内部实际是转换成String存取
JsonArray put(String key,JsonArray value) getJsonArray(String key) 内部实际是转换成String存取
byte[] put(String key,byte[] bytes) getBytes(String key) 存图片用这个实现,大家自行封装啦
Serializable put(String key,Serializable value) getSerializable(String key) 返回的是一个泛型对象

manager.flush() 方法推荐在需要缓存的界面的 onpause() 方法中调用,它的作用是同步缓存的日志文件,没必要每次缓存都调用

最后

觉得本文对你有帮助
关注简书PandaQ404,持续分享中。。。
Github主页

上一篇下一篇

猜你喜欢

热点阅读