Android 的缓存技术

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

内存缓存

读取速度快

可分配空间小

有被系统回收风险

应用退出就没有了,无法做到离线缓存

磁盘缓存

读取速度比内存缓存慢

可分配空间较大

不会因为系统内存紧张而被系统回收

退出应用缓存仍然存在(缓存在应用对应的磁盘目录中卸载时会一同清理,缓存在其他位置卸载会有残留)

本文主要介绍磁盘缓存,并以缓存 MVPDemo 中的知乎日报新闻条目作为事例展示如何使用磁盘缓存对新闻列表进行缓存。

DiskLruCache

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

DiskLruCache 使用时需要注意:

每一条缓存都有一个 String 类型的 key 与之对应,每一个 key 中的值都必须满足 [a-z0-9_-]{1,120}的规则即数字大小写字母长度在1-120之间,所以推荐将字符串譬如图片的 url 等进行 MD5 加密后作为 key。

DiskLruCache 的数据是缓存在文件系统的某一目录中的,这个目录必须是唯一对应某一条缓存的,缓存可能会重写和删除目录中的文件。多个进程同一时间使用同一个缓存目录会出错。

DiskLruCache 遵从 LRU 算法,当缓存数据达到设定的极限值时将会后台自动按照 LRU 算法移除缓存直到满足存下新的缓存不超过极限值。

一条缓存记录一次只能有一个 editor ,如果值不可编辑将会返回一个空值。

当一条缓存创建时,应该提供完整的值,如果是空值的话使用占位符代替。

如果文件从文件系统中丢失,相应的条目将从缓存中删除。如果写入缓存值时出错,编辑将失败。

使用方法

打开缓存

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 stories = (ArrayList) ois.readObject();复制代码

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

完整流程的代码

//使用rxandroid+retrofit进行请求

public void loadDataByRxandroidRetrofit(){

mINewsListActivity.showProgressBar();

Subscription subscription = ApiManager.getInstence().getDataService()

.getZhihuDaily()

.map(new Func1>() {

@Override

public ArrayList call(ZhiHuDaily zhiHuDaily){

ArrayList stories = zhiHuDaily.getStories();

if (stories != null) {

//加载成功后将数据缓存倒本地(demo 中只有一页,实际使用时根据需求选择是否进行缓存)

makeCache(zhiHuDaily.getStories());

}

return stories;

}

})

//设置事件触发在非主线程

.subscribeOn(Schedulers.io())

//设置事件接受在UI线程以达到UI显示的目的

.observeOn(AndroidSchedulers.mainThread())

.subscribe(new Subscriber>() {

@Override

public void onCompleted(){

mINewsListActivity.hidProgressBar();

}

@Override

public void onError(Throwable e){

mINewsListActivity.getDataFail("", e.getMessage());

}

@Override

public void onNext(ArrayList stories){

mINewsListActivity.getDataSuccess(stories);

}

});

//绑定观察对象,注意在界面的ondestory或者onpouse方法中调用presenter.unsubcription();

addSubscription(subscription);

}

//生成Cache

private void makeCache(ArrayList 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 stories = (ArrayList) 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主页

android强制缓存写磁盘,优雅的构建 Android 项目之磁盘缓存(DiskLruCache)相关推荐

  1. Android开发如何写出优雅的代码

    很多时候我们去面试,人家总会问一个问题,你们公司开发一个app是如何进行技术选择的,app中涉及到了哪些开发模式,谈谈你对mvc.mvp和mvvm的区别.或许在这些问题上每个人有每个人的看法,在我看来 ...

  2. android米聊手写和涂鸦源码,Android访米聊手写和涂鸦源码

    Android访米聊手写和涂鸦源码 \请下载源代码,只上传Android访米聊手写和涂鸦源码源程序列表内容,如果需要此程序,请点击-下载,下载需要资料源代码. Android访米聊手写和涂鸦源码.ra ...

  3. 《Android Studio实战 快速、高效地构建Android应用》--二、在Android Studio中编程

    代码折叠 Ctrl+数字加号展开光标处已折叠代码块 Ctrl+数字减号折叠光标处已展开代码块 Ctrl+Shift+数字加号展开窗口中全部代码 Ctrl+Shift+数字减号折叠窗口中全部代码 注释代 ...

  4. Android官方技术文档翻译——开发工具的构建概述

    本文译自Android官方技术文档<Build Overview>,原文地址:http://tools.android.com/build. 因为<Android Lint Chec ...

  5. 构建 django项目

    使用Pycharm 专业版构建 django项目 File -> new Project -> Django -> 设置项目名 及 虚拟环境 -> 创建即可 (在新建项目的过程 ...

  6. android强制缓存写磁盘,Android缓存之DiskLruCache磁盘缓存的使用

    DiskLruCache和LruCache不同的是,LruCache是内存缓存,而DiskLruCache是指磁盘缓存,顾名思义就是把文件缓存到磁盘,也也就是手机的内存卡中.接下来先简单介绍DiskL ...

  7. android glide设置缓存大小,Glide4-入门教程-5-缓存机制(内存缓存和磁盘缓存)

    一.简介 这一节,主要是讲glide4的缓存机制.Glide的缓存设计是非常的先进的,考虑的场景也很周全.Glide 的缓存分为两种,一是内存缓存,另一个是硬盘缓存. 这两种缓存的作用各不相同,内存缓 ...

  8. 如何写出优雅的 Golang 代码

    Go 语言是一门简单.易学的编程语言,对于有编程背景的工程师来说,学习 Go 语言并写出能够运行的代码并不是一件困难的事情,对于之前有过其他语言经验的开发者来说,写什么语言都像自己学过的语言其实是有问 ...

  9. Android 开源项目android-open-project工具库解析之(一) 依赖注入,图片缓存,网络相关,数据库orm工具包,Android公共库...

    一.依赖注入DI 通过依赖注入降低View.服务.资源简化初始化.事件绑定等反复繁琐工作 AndroidAnnotations(Code Diet) android高速开发框架 项目地址:https: ...

最新文章

  1. Ubuntu阿里云搭建Mono.net环境
  2. iOS开发网络篇—GET请求和POST请求
  3. visual c++ 6.0原版_C/C++编程笔记:C语言函数指针的理解与使用,就是这么简单明了!...
  4. java8新特性_乐字节-Java8新特性-接口默认方法
  5. wordpress留言板comments.php添加自定义字段,php – 如何自定义WordPress comment_form();
  6. 【js拾遗】名称空间
  7. HTML5为输入框添加语音输入功能
  8. 英伟达赚钱能力创历史新高,老黄:GPU供不应求我也很急
  9. SparkSQL中数据集类的封装
  10. Android7.0的xposed框架,Android 7.x 安装Xposed框架
  11. 使用python+Pyqt5来写一个简易串口调试助手
  12. excel wind插件使用_Python和wind的交互—在债券中的应用
  13. ps还原上一步快捷键,ps返回上一步快捷键是什么
  14. 3、MybatisPlus
  15. java guardedby_java 多线程并发设计模式之三:Guarded suspension 模式
  16. sam格式的结构和意义_SAM文件是什么
  17. 解决win10一开机内存(8G)就占用70%多,查看任务管理器并没有占用内存很高的进程的问题
  18. 操作系统,计算机网络,数据库刷题笔记10
  19. R语言箱线图添加显著性--不同水平实现方法
  20. Gingko Framework:页面参数中文乱码解决

热门文章

  1. 迟到的小熊 计算机画图画教案,课题(5、下):《迟到及小熊—认识计算机画图软件》第一周.doc...
  2. python输入姓名输出职务_python题中输入姓名的笔画数,输出所属性格,及该种性格人的典型代表?...
  3. android动画ppt整理
  4. 不被多数人知道但却超好的东东····生活小常识
  5. 达芬奇模板-梦幻棱镜光效折射视觉特效预设Prismatic Effects
  6. matlab解决excel 导入数据精度的问题
  7. mysql无法停止slave_stop slave卡住
  8. 看不懂,为什么跑出租车的大战送外卖的,没关系,主要是你不理解大数据
  9. js逆向第1例:淘宝逛逛H5页面SIGN加密算法
  10. 【转】刘铁岩:在微软大学的三次华丽转型