三分钟学会缓存工具DiskLruCache

DiskLruCache是一个十分好用的android缓存工具,我们可以从GitHub上下载其源码:https://github.com/JakeWharton/DiskLruCache

DiskLruCache所有的数据都存储在/storage/emulated/0/Android/data/应用包名/cache/XXX文件夹中(你也可以修改,但不建议这样做,原因请继续往下看),这个是android系统默认的应用缓存位置,如果应用被删除,这个文件也会一起被删除,避免应用删除后有残留数据的问题。同时,由于数据没有存储在硬盘里,所以不会影响系统性能,在sd卡里,你可以存储任意多数据。
由于DiskLruCache是被final修饰的,因此不可以直接通过new获得它的实例,我们使用它的open方法获得它的一个实例:
public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize)
open方法需要四个参数,第一个是缓存文件文件的位置,通过下面的方法可得到:

    private File getDiskCacheDir(Context context, String uniqueName) {String cachePath;//如果sd卡存在并且没有被移除if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())|| !Environment.isExternalStorageRemovable()) {cachePath = context.getExternalCacheDir().getPath();} else {cachePath = context.getCacheDir().getPath();}return new File(cachePath + File.separator + uniqueName);}

第二个参数是应用程序的版本号,要传入版本号是因为如果应用升级缓存会被清除掉。通过下面的方法可以获得程序的版本号:

    private int getAppVersion(Context context) {try {PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);return info.versionCode;} catch (NameNotFoundException e) {e.printStackTrace();}return 1;}

第三个参数表示同一个key可以对应多少个缓存文件,一般情况下我们都是传1,这样key和缓存文件一一对应,查找和移除都会比较方便。
第四个参数表示最大可以缓存多少字节的数据。
打开了DiskLruCache之后,我们可以看看怎么向DiskLruCache中缓存数据:
先来看看从网上down一张图片:

    private boolean downloadImg(final String urlStr,final OutputStream outputStream) {HttpURLConnection conn = null;BufferedOutputStream out = null;BufferedInputStream in = null;try {URL url = new URL(urlStr);conn = (HttpURLConnection) url.openConnection();in = new BufferedInputStream(conn.getInputStream(), 8 * 1024);out = new BufferedOutputStream(outputStream, 8 * 1024);int len = 0;while ((len = in.read()) != -1) {out.write(len);}return true;} catch (IOException e) {e.printStackTrace();} finally {if (conn != null)conn.disconnect();try {if (out != null)out.close();} catch (IOException e) {e.printStackTrace();}try {if (in != null)in.close();} catch (IOException e) {e.printStackTrace();}}return false;}

这是一个简单的联网down图片代码,拿到图片后就可以缓存到本地了,但是对于每一个存储资源都需要有一个key,这个key要是唯一的,而且这个key最长120个字符,且只能包括a-z,0-9,下划线以及减号,一次我们可以采用Java中的UUID来得到key,也可以使用MD5加密网址得到一个key,我这里采用md5,方法如下:

public class MD5Util {public final static String md5(String pwd) {//用于加密的字符char md5String[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9','A', 'B', 'C', 'D', 'E', 'F' };try {//使用平台的默认字符集将此 String 编码为 byte序列,并将结果存储到一个新的 byte数组中byte[] btInput = pwd.getBytes();// 获得指定摘要算法的 MessageDigest对象,此处为MD5//MessageDigest类为应用程序提供信息摘要算法的功能,如 MD5 或 SHA 算法。//信息摘要是安全的单向哈希函数,它接收任意大小的数据,并输出固定长度的哈希值。 MessageDigest mdInst = MessageDigest.getInstance("MD5");//System.out.println(mdInst);  //MD5 Message Digest from SUN, <initialized>//MessageDigest对象通过使用 update方法处理数据, 使用指定的byte数组更新摘要mdInst.update(btInput);//System.out.println(mdInst);  //MD5 Message Digest from SUN, <in progress>// 摘要更新之后,通过调用digest()执行哈希计算,获得密文byte[] md = mdInst.digest();//System.out.println(md);// 把密文转换成十六进制的字符串形式int j = md.length;//System.out.println(j);char str[] = new char[j * 2];int k = 0;for (int i = 0; i < j; i++) {   //  i = 0byte byte0 = md[i];  //95str[k++] = md5String[byte0 >>> 4 & 0xf];    //    5  str[k++] = md5String[byte0 & 0xf];   //   F}//返回经过加密后的字符串return new String(str);} catch (Exception e) {e.printStackTrace();return null;}}
}

各位看官在使用的时候记得把md5String[]中大写的字母改为小写,因为key中如果有大写字母验证会不通过。当然,你也可以修改DiskLruCache的源码从而让它支持大写字母,修改的地方:
这里写图片描述
现在万事俱备,我们来把图片缓存起来,由于联网是好事操作,所以要在新线程中完成,完整的方法如下:

    private void cacheImg() {new Thread(new Runnable() {@Overridepublic void run() {String key = MD5Util.md5(IMGIP);try {DiskLruCache.Editor editor = mDiskLruCache.edit(key);if (editor != null) {OutputStream out = editor.newOutputStream(0);if (downloadImg(IMGIP, out)) {//提交editor.commit();} else {//撤销操作editor.abort();}}/*** 这个方法用于将内存中的操作记录同步到日志文件(也就是journal文件)当中。* 这个方法非常重要,因为DiskLruCache能够正常工作的前提就是要依赖于journal文件中的内容。* 并不是每次写入缓存都要调用一次flush()方法的,频繁地调用并不会带来任何好处,* 只会额外增加同步journal文件的时间。* 比较标准的做法就是在Activity的onPause()方法中去调用一次flush()方法就可以了*/mDiskLruCache.flush();} catch (IOException e) {e.printStackTrace();}}}).start();}

editor.newOutputStream(0);方法有一个参数,查看源码我们知道这个参数必须大于0并且小于valueCount,前文中valueCount我们已经设置为1了,所以这里只能取值0。这个时候打开你的缓存文件夹,/storage/emulated/0/Android/data/应用包名/cache/XXX,里边已经有了我们缓存的数据了:
这里写图片描述

好了,数据存下来了,接下来就是读取,每一个缓存文件都对应一个key,读取就是根据这个key来读取:

    private void showImg() {String key = MD5Util.md5(IMGIP);  try {DiskLruCache.Snapshot snapShot = mDiskLruCache.get(key);if(snapShot!=null){InputStream is = snapShot.getInputStream(0);Bitmap bitmap = BitmapFactory.decodeStream(is);im.setImageBitmap(bitmap);}} catch (IOException e) {e.printStackTrace();} }

读取的时候我们最先拿到的是一个Snapshot 对象,再根据我们之前传入的参数0拿到缓存文件的流,最后把流转换为图片。

到这里大家可能就明白了,之前的editor.newOutputStream(0);方法为什么会有一个0的参数了,相当于一个标识,读取时也传入参数0才能读到我们想要的数据。(加入我们的key与缓存文件不是一一对应,也就是我们一开始的open方法中传入的不是valueCount的值不是1,那么一个key对应多个缓存文件我们要怎么区分?就是通过这种方式,有兴趣的同学查看源码就一目了然了)。

下来就是清除缓存了,看方法:

    private void clearCache() {String key = MD5Util.md5(IMGIP);try {mDiskLruCache.remove(key);} catch (IOException e) {e.printStackTrace();}  }

根据缓存文件的key,调用remove方法,将该缓存文件移除。

下来是查看缓存大小:
这里写图片描述
像凤凰新闻客户端中显示缓存大小,这个数值我们可以通过size()方法直接拿到:

    private void getCacheSize() {tv.setText(mDiskLruCache.size()+"");}

大家应该看到了凤凰新闻还有一个功能就是清除缓存,这个功能直接调用delete方法就能实现:

    private void deleteAll() {/*** 这个方法用于将所有的缓存数据全部删除* 其实只需要调用一下DiskLruCache的delete()方法就可以实现了。* 会删除包括日志文件在内的所有文件*/try {mDiskLruCache.delete();} catch (IOException e) {e.printStackTrace();}}

所有功能都完成之后,我们要记得在onDestory方法中关闭DiskLruCache。

    @Overrideprotected void onDestroy() {super.onDestroy();/*** 这个方法用于将DiskLruCache关闭掉,是和open()方法对应的一个方法。* 关闭掉了之后就不能再调用DiskLruCache中任何操作缓存数据的方法,* 通常只应该在Activity的onDestroy()方法中去调用close()方法。*/try {mDiskLruCache.close();} catch (IOException e) {e.printStackTrace();}}

到此,我想大家已经基本会用这个东东了吧。

最后奉上本文源码下载地址http://pan.baidu.com/s/1kTzSHtd

版权声明:本文为博主原创文章,未经博主允许不得转载。若有错误地方,还望批评指正,不胜感激。

转载于:https://www.cnblogs.com/lenve/p/4770527.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/290732.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【数据挖掘】知识总结——背景、定义、一般流程及应用(一)

数据挖掘知识总结&#xff08;一&#xff09; 1.数据挖掘产生的背景&&驱动力 DRIP&#xff08;Data Rich Information Poor&#xff09; 四种主要技术激发了人们对数据挖掘技术的开发、应用和研究的兴趣&#xff1a; 超大规模数据库的出现&#xff0c;如商业数据仓…

LinkedIn联合创始人:硅谷也就700万人,为什么能创建这么多瞩目的公司 ?

很多人不解&#xff1a;现在创业公司这么多&#xff0c;在世界任何地方都有很多人懂技术&#xff0c;营销&#xff0c;也有VC&#xff0c;可以组建团队&#xff0c;那为啥非要在硅谷做呢&#xff1f;作者董飞&#xff0c;整理了 Linkedin 创始人 Reid Hoffman 在CS183C 课程的分…

Docker容器安全的8大风险和33个最佳实践丨IDCF

作者&#xff1a;StackRox译者&#xff1a;冬哥原文&#xff1a;https://www.stackrox.io/blog/docker-security-101/容器以及例如Kubernetes等编排器开启了应用程序开发方法的新时代&#xff0c;支持微服务架构以及持续开发和交付。根据我们最新的容器状态和 Kubernetes 安全报…

iOS应用开发的五个Java开源工具

随着第三方工具的不断壮大&#xff0c;开发人员逐渐摆脱政策束缚&#xff0c;对于iOS系统的封闭性为其他语言&#xff08;如Java&#xff09;开发者诟病得到解脱&#xff0c;开始使用自己熟悉的语言来编写iOS本地应用&#xff0c;或将其他平台上的应用移植到iOS上。 本文为你介…

【Tensorflow】解决No module named ‘matplotlib‘/‘pandas‘

用Tensorflow出现No module named ‘matplotlib’/‘pandas’ 尝试网上方法打开终端pip install pandas 不成功&#xff0c;报一大堆红字错误。自己尝试了一种方法。 打开Anaconda Navigator&#xff1b;找到Environments&#xff0c;点击对应环境&#xff0c;我的是tensorfl…

人之将死其言也善?30年来死囚遗言分析

今天是感恩节&#xff0c;不知道这个话题合适不合适。我们经常会提到『死而无憾』这个词。这个词似乎是一种理想状态&#xff0c;几乎100%的人还是做不到的。那么我们的『憾』在哪里&#xff1f;这个问题看似不是难题&#xff0c;不过仔细想想是永远没答案的&#xff0c;人只有…

如何通过 C# 比较两幅图片的相似度?

咨询区 Byyo我在用 C# 实现一个可以查找重复图片的小工具&#xff0c;我目前是给每一个图片做一个 md5 码&#xff0c;然后通过 md5 值来判断图片是否相同。但现实情况要复杂的多&#xff0c;比如&#xff1a;图片被旋转了&#xff0c;比如&#xff1a;90图片大小不一致不同的压…

linux之用openssl命令Base64编码解码、md5/sha1摘要、AES/DES3加密解密

1、我们先看openssl help命令会输出什么? 2、我们用openssl命令实现字符串和文本的Base64编码和解码 openssl base64 openssl base64 -d 3、我们用openssl命令实现字符串和文本的md5/sha1摘要

陈松松:如何锁定细分领域,视频营销才更容易持续做下去

每个视频&#xff0c;都是你的金牌业务员这是我写的第33篇视频营销原创文章与其搜索十年&#xff0c;不如花一年的时间学习&#xff0c;去赚9年的高薪&#xff01;很多朋友都在探讨&#xff0c;视频营销越来越难了&#xff0c;得转行了&#xff01;我想说的是&#xff0c;自己不…

转:工作流服务Workflow Service(1):ReceiveActivity

转&#xff1a;http://www.cnblogs.com/carysun/archive/2009/01/11/receiveactivity.html 在.NET3.5中WF提供了和WCF的整合&#xff0c;就是工作流服务&#xff0c;即使用工作流创作的 WCF服务。服务协定的实现是通过一个或多个 ReceiveActivity 活动处理的。在WCF中提供了三种…

Echart..js插件渲染报错 data.length1?

问题 getJSON提交 返回数据正常&#xff0c;在传入参数进行序列化&#xff0c;渲染报表时报错 option.data.length < 1. 分析 1.可能情况一: . 可自己明明是getJSON()把渲染放在成功回调函数里面了&#xff0c;所以显然不是这个错误 2.可能情况二 &#xff1a; 序列化数据没…

《JavaScript专家编程》——第1章 对象和原型 1.1鸟瞰JavaScript

本节书摘来自异步社区《JavaScript专家编程》一书中的第1章&#xff0c;第1.1节&#xff0c;作者&#xff1a;【美】Mark Daggett&#xff08;达格特&#xff09;著&#xff0c;更多章节内容可以访问云栖社区“异步社区”公众号查看 第1章 对象和原型 练习不会造就完美&#xf…

c#使用PdfiumViewer展示、打印pdf文档

1:简介PdfiumViewer 是一个 WinForms 控件&#xff0c;它承载一个 PdfRenderer 控件并添加一个工具栏来保存或打印 PDF 文件2:兼容性除了常规的win7 win10 也支持xp win83:对比Spire.Pdf和Adobe PDF ReaderSpire.Pdf收费且免费版只能打印三页的pdfAdobe PDF Reader每台电脑都必…

apache 静态编译和动态编译参考

apache-2.2.22 编译安装笔记 一、静态编译 在使用./configure 编译的时候&#xff0c;即没有使用--enable-mods-shared[module]或者--enable-[module]shared这2个中的一个&#xff0c;那么所有的默认模块为静态。何谓静态&#xff1f; 其实就是编译的时候所有的模块自己编译进h…

jQuery插件ASP.NET应用之AjaxUpload

本次使用AJAXUPLOAD做为上传客户端无刷上传插件&#xff0c;其最新版本为3.9&#xff0c;官方地址&#xff1a;http://valums.com/ajax-upload/ 在页面中引入 jquery.min.1.4.2.js 和 ajaxupload.js Html代码 <script src"Scripts/jquery-1.4.2.min.js" type&quo…

Avalonia跨平台入门第十一篇之自定义Window

在前面分享的几篇中咱已经玩耍了Popup、ListBox多选、Grid动态分、RadioButton模板、控件的拖放效果、控件的置顶和置底、控件的锁定;今天趁着周末接着去摸索自定义Window样式,毕竟自带的样式不太让人满意;最终实现的效果如下图:前台Window样式:后台Window代码:咱也基于AvalonS…

ie8下修改input的type属性报错

摘要&#xff1a; 现在有一个需求如图所示&#xff0c;当用户勾选显示明文复选框时&#xff0c;要以明文显示用户输入的密码&#xff0c;去掉勾选时要变回密文&#xff0c;刚开始想到的就是修改输入框的type来决定显示明文还是密文&#xff0c;使用jQuery的attr来做试验&#x…

dotNet Core使用SignalR实现websocket

前言:最近有一个公司项目做一个排队叫号系统&#xff0c;系统功能不复杂&#xff0c;所以后端就我一人&#xff0c;难点在于消息推送到安卓屏上&#xff0c;最近有点时间&#xff0c;把我工作中使用的技术分享出来&#xff01;整个技术架构:前端使用vue uniapp&#xff0c;后端…

《手机测试Robotium实战教程》——导读

本节书摘来自异步社区《手机测试Robotium实战教程》一书中的导读&#xff0c;作者 杨志伟&#xff0c;更多章节内容可以访问云栖社区“异步社区”公众号查看 目 录前 言第1章 自动化测试简介 1.1节何为自动化测试1.2节自动化测试和手动测试的对比1.3节移动端自动化测试工具的选…

为record类型自定义Equals方法

前言record类型&#xff0c;这是一种新引用类型&#xff0c;而不是类或结构。record与类不同&#xff0c;区别在于record类型使用基于值的相等性。例如&#xff1a;public record DemoRecord(int id);public class DemoClass {public DemoClass(int id){this.id id;}public in…