某宅的 Android 学习笔记(四)——用 DiskLruCache 实现本地缓存
2016-08-29 22:06
525 查看
为什么要用DiskLruCache?
离线数据对于依赖网络加载数据的APP来说具有很重要的意义,当无网络或者是网络状况不好时,APP依然具备部分功能是一种很好的用户体验。假设网易新闻这类新闻客户端,数据完全存储在缓存中而不使用DiskLruCache技术存储,那么当客户端被销毁,缓存被释放,意味着再次打开APP将是一片空白。DiskLruCache是Google官方推荐的一套硬盘缓存的API,虽然Android中并没有包含她,但是能得到官方推荐,必然有着过人之处。将DiskLruCache加入项目
首先我们需要下载DiskLruCache的文件,github。不过如果用的是AndroidStudio的话,只需要在build.gradle中添加如下代码即可:compile 'com.jakewharton:disklrucache:2.0.2'
接着重新构建gradle文件就可以了。
使用DiskLruCache
1.创建DiskLruCache实例
DiskLruCache是不能直接new的,如果要创建一个DiskLruCache的实例,需要调用它的open()方法。open()方法接收四个参数,第一个参数指定的是数据的缓存地址,第二个参数指定当前应用程序的版本号,第三个参数指定同一个key可以对应多少个缓存文件,基本都是传1,第四个参数指定最多可以缓存多少字节的数据。那么缓存地址如何获得呢?当然可以直接制定,但是为了用户体验考虑,不建议这么做。一般会放在/sdcard/Android/data//cache 下,因为选择在这个位置有两点好处:第一,这是存储在SD卡上的,因此即使缓存再多的数据也不会对手机的内置存储空间有任何影响,只要SD卡空间足够就行。第二,这个路径被Android系统认定为应用程序的缓存路径,当程序被卸载的时候,这里的数据也会一起被清除掉,这样就不会出现删除程序之后手机上还有很多残留数据的问题。
但是考虑到不是所有的手机都一定会有SD卡,这里还是需要做一下判断。
/** * 获取缓存目录 * * @param context * @param uniqueName 用于区分缓存内容 * @return */ 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); }
获取版本号。每当版本号改变,缓存路径下存储的所有数据都会被清除掉,因为DiskLruCache认为当应用程序有版本更新的时候,所有的数据都应该从网上重新获取。
/** * 获取应用版本号 * * @param context * @return */ private int getAppVersion(Context context) { try { PackageInfo info = context.getPackageManager().getPackageInfo( context.getPackageName(), 0); return info.versionCode; } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); } return 1; }
参数准备好了,我们就可以得到DiskLruCache的实例啦:
private DiskLruCache diskLruCache; //获取 DiskLruCache 的实例 try { File cacheFile = getDiskCacheDir(listView.getContext(), "bitmap"); if (!cacheFile.exists()) { cacheFile.mkdirs(); } diskLruCache = DiskLruCache.open(cacheFile, getAppVersion(listView.getContext()), 1, 10 * 1024 * 1024); } catch (IOException e) { e.printStackTrace(); }
2.获取DiskLruCache.Editor与DiskLruCache.Snapshot
DiskLruCache.Editor可以得到一个输出流outputStream,我们可以通过这个输出流将网络请求得到的数据写入本地。网络请求方法如下:/** * 开启网络请求 * 根据url来下载图片 */ private boolean downloadUrlToStream(String urlString, OutputStream outputStream) { HttpURLConnection connection = null; BufferedInputStream in = null; BufferedOutputStream out = null; try { final URL url = new URL(urlString); connection = (HttpURLConnection) url.openConnection(); in = new BufferedInputStream(connection.getInputStream()); out = new BufferedOutputStream(outputStream); int b; while ((b = in.read()) != -1) { out.write(b); } return true; } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if (connection != null) { connection.disconnect(); } try { if (out != null) { out.close(); } if (in != null) { in.close(); } } catch (IOException e) { e.printStackTrace(); } } return false; }
获取Editor的edit()方法接收一个参数key,这个key将会成为缓存文件的文件名,并且必须要和图片的URL是一一对应的。但是图片URL中可能包含一些特殊字符,这些字符有可能在命名文件时是不合法的。不过可以将图片的URL进行MD5编码,编码后的字符串肯定是唯一的,并且只会包含0-F这样的字符,完全符合文件的命名规则。
/** * 为了保证文件命名合法,使用 MD5 编码 url */ private String hashKeyForDisk(String url) { String cacheKey; try { final MessageDigest digest = MessageDigest.getInstance("MD5"); digest.update(url.getBytes()); cacheKey = byteToHexString(digest.digest()); } catch (NoSuchAlgorithmException e) { cacheKey = String.valueOf(url.hashCode()); } return cacheKey; } private String byteToHexString(byte[] bytes) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < bytes.length; i++) { String hex = Integer.toHexString(0xFF & bytes[i]); if (hex.length() == 1) { sb.append('0'); } sb.append(hex); } return sb.toString(); }
这样只需要传入key,就能得到Editor对象了。不过要读取缓存,我们还需要Snapshot对象。要得到Snapshot很简单:
DiskLruCache.Snapshot snapShot = mDiskLruCache.get(key);
接着调用它的getInputStream()方法就可以得到缓存文件的输入流了。同样地,getInputStream()方法也需要传一个index参数,这里传入0就好。
图片加载的方法就可以写成这样:(这里用了AsyncTask,由于要开启网络请求,所以必须放在子线程中处理)
@Override protected Bitmap doInBackground(String... params) { String url = params[0]; String key = hashKeyForDisk(url); Bitmap bitmap = null; InputStream in = null; try { //获取 snapshot 对象 snapshot = diskLruCache.get(key); //如果为空,则需要从网络下载图片,并存入缓存 if (snapshot == null) { editor = diskLruCache.edit(key); if (editor != null) { OutputStream outputStream = editor.newOutputStream(0); if (downloadUrlToStream(url, outputStream)) { editor.commit(); } else { editor.abort(); } } //同步文件记录 diskLruCache.flush(); //下载完成后,重新获取 snapshot 对象 snapshot = diskLruCache.get(key); } //从 snapshot 中获取 bitmap if (snapshot != null) { in = snapshot.getInputStream(0); bitmap = BitmapFactory.decodeStream(in); } //将 bitmap 存入内存缓存中 if (bitmap != null) { addBitmapToLruCache(url, bitmap); } return bitmap; } catch (IOException e) { e.printStackTrace(); } finally { try { if (in != null) { in.close(); } } catch (IOException e) { e.printStackTrace(); } } return null; }
首次启动时的图片加载方法:
/** * 若缓存中存在bitmap,直接设置 * 反之设置默认的本地图片 */ public void showImg(ImageView imageView, String url) { //优先从缓存获取图片 Bitmap bitmap = getBitmapFromLruCache(url); try { if (bitmap == null) { snapshot = diskLruCache.get(hashKeyForDisk(url)); if(snapshot != null){ bitmap = BitmapFactory.decodeStream(snapshot.getInputStream(0)); } } } catch (IOException e) { e.printStackTrace(); } //若bitmap存在,直接设置 if (bitmap != null) { imageView.setImageBitmap(bitmap); } else { //初始化ImageView的图片,未加载网络图片时显示 imageView.setImageResource(R a0d3 .mipmap.ic_launcher); } }
示例如下,可以看到成功从本地取出了缓存的图片。
最后附上自己项目的地址:https://github.com/Zhai-Wang/KanZhiHu,欢迎各路大神指正!
相关文章推荐
- Android本地缓存DiskLruCache完整详细学习示例
- Android本地缓存DiskLruCache完整详细学习示例
- 使用diskLruCache缓存数据到本地(个人笔记)
- Android中Bitmap的加载和Cache(三级缓存 :LruCache,DiskLruCache)学习笔记
- Android之本地缓存——LruCache(内存缓存)与DiskLruCache(硬盘缓存)统一框架
- Android 实现图片缓存异步加载框架学习笔记
- Android (DiskLruCache)硬盘缓存代码实现
- Android 实现内存+SD卡 图片缓存策略 (LurCache+DiskLruCache)
- Android 缓存策略LruCache和DiskLruCache学习
- Android之okHttpClient+handler+LruCache缓存网络图片学习笔记(通用MVP模式)
- iOS学习笔记22—ASIHTTPRequest和ASIDownloadCache实现本地缓存
- 读Android开发艺术探索笔记(LruCache+DiskLruCache实现强大的图片加载)
- Android硬盘缓存技术DiskLruCache技术笔记
- Android Volley图片缓存机制结合DiskLruCache实现磁盘缓存
- android菜鸟学习笔记28----Android中的Service生命周期及本地和远程服务绑定的实现
- Android Animation学习笔记 Posted on 2010-01-11 23:00 feisky 阅读(40227) 评论(12) 编辑 收藏 关于动画的实现,Android提供了A
- android异步加载图片并缓存到本地实现方法
- Android仿人人客户端(v5.7.1)——对从服务器端(网络)获取的图片进行本地双缓存处理(编码实现)
- Android图片异步加载与本地缓存的实现
- android实现服务器图片本地缓存