自己写Android图片缓存框架之二级disk缓存
来源:互联网 发布:手机淘宝买东西教程 编辑:程序博客网 时间:2024/06/05 19:35
上一节中已经运用Lru算法实现了内存缓存,在从桌面回到前台时可以快速的从内存中进行加载图片,但是如果应用被系统回收或人为的主动清除这样还是会从网络加载,所以我们不仅需要缓存在内存中,还要在磁盘中进行缓存,这样如果内存没有就从磁盘中进行读取数据。
这里我们使用google提供的DiskLruCache来实现disk缓存,由于源码过长就不贴了,所有的代码包括图片加载的demo已经上传到github上。我们将DiskLruCache直接拷贝到项目代码中,并将原来的包名libcore.io更改为我们的包名。
DiskLruCache提供了一系列方法,先依次分析一下
private DiskLruCache(File directory, int appVersion, int valueCount, long maxSize) { this.directory = directory; this.appVersion = appVersion; this.journalFile = new File(directory, JOURNAL_FILE); this.journalFileTmp = new File(directory, JOURNAL_FILE_TMP); this.valueCount = valueCount; this.maxSize = maxSize; }可以看到将DiskLruCache设为了private,这样我们就不能直接new出对象了,而是直接提供了一个open方法来返回对象
/** * 根据地址打开缓存空间,如果不存在就创建一个 * * @param directory 数据缓存地址 * @param appVersion 当前app版本 * @param valueCount 一个key值对应多少个缓存文件 一般传1 * @param maxSize 最多可以缓存多少字节的数据 一般为10M * @throws java.io.IOException if reading or writing the cache directory fails */ public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize) throws IOException { if (maxSize <= 0) { throw new IllegalArgumentException("maxSize <= 0"); } if (valueCount <= 0) { throw new IllegalArgumentException("valueCount <= 0"); } //调用构造函数得到一个cache对象 DiskLruCache cache = new DiskLruCache(directory, appVersion, valueCount, maxSize); if (cache.journalFile.exists()) { try { cache.readJournal(); cache.processJournal(); cache.journalWriter = new BufferedWriter(new FileWriter(cache.journalFile, true), IO_BUFFER_SIZE); return cache; } catch (IOException journalIsCorrupt) { cache.delete(); } } directory.mkdirs(); cache = new DiskLruCache(directory, appVersion, valueCount, maxSize); cache.rebuildJournal(); return cache; }DiskLruCache提供了一个flush方法,为了防止频繁的写数据,建议在Activity的onPause时调用一次flush()方法,除此之外我们需要对url进行MD5加密,防止url出现字符不合法的情况,MD5算法在大数据的hash碰撞上性能也比较良好,编码后每个字符都在0到F之间,符合要求。
/** * 使用MD5算法对传入的key进行加密,以免出现url不合法 * @param key * @return */public String hashKeyForDisk(String key){String cacheKey;try {MessageDigest digest = MessageDigest.getInstance("MD5");digest.update(key.getBytes());cacheKey = bytesToHexString(digest.digest());} catch (NoSuchAlgorithmException e) {cacheKey = String.valueOf(key.hashCode());e.printStackTrace();}return cacheKey;}private String bytesToHexString(byte[] bytes){StringBuilder builder = new StringBuilder();for (int i = 0; i < bytes.length; i++) {String hex = Integer.toHexString(0xFF & bytes[i]);if (hex.length() == 1) {builder.append("0");}builder.append(hex);}return builder.toString();}
磁盘的缓存路径一般是在内存卡中,但是现在的一些手机可能并没有内存卡,所以我们需要动态的加载存储路径,
/** * 根据传入的unique返回硬盘缓存地址 * * @param context * @param uniqueName * @return */public File getDiskCacheDir(Context context, String uniqueName) {String cachePath;if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())|| !Environment.isExternalStorageRemovable()) {//当有SD卡时cachePath = context.getExternalCacheDir().getPath();} else {//当没有SD卡或SD卡被移除cachePath = context.getCacheDir().getPath();}return new File(cachePath + File.separator + uniqueName);}另外有些图片可能会过大比如远超要显示的大小,这样不仅因为图片过大在下载时比较慢,而且容易造成OOM,所以我们需要对图片进行压缩,让其显示匹配手机显示的大小
/** * 计算目标图片缩放比例 * @param options * @param reqWidth * @param reqHeight * @return */public static int calculateInSampleSize(BitmapFactory.Options options,int reqWidth,int reqHeight){int height = options.outHeight;int width = options.outWidth;int inSampleSize = 1;if (height > reqHeight || width > reqWidth) {//计算高度和宽度对目标高宽的比例int heightRadio = Math.round(height/reqHeight);int widthRadio = Math.round(width/reqWidth);//选择高宽中较小的一个作为压缩比例,保证图片比目标尺寸大inSampleSize = heightRadio < widthRadio ? heightRadio : widthRadio;}return inSampleSize;}public static Bitmap decodeSampledBitmap(FileDescriptor descriptor,int reqWidth,int reqHeight){BitmapFactory.Options options = new BitmapFactory.Options();//加载时将injustdecodebounds设置为true,获取图片大小options.inJustDecodeBounds = true;BitmapFactory.decodeFileDescriptor(descriptor, null, options);//计算压缩比例options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); options.inJustDecodeBounds = false;return BitmapFactory.decodeFileDescriptor(descriptor, null, options);}在将图片写入disk缓存中时,我们需要获取一个DiskLruCache.Editor的editor来将流写入缓存中
@Overrideprotected Bitmap doInBackground(String... params) {imageUrl = params[0];FileDescriptor descriptor = null;FileInputStream inputStream = null;Snapshot snapshot = null;try {//生成图片对应的keyString key = imageLoader.hashKeyForDisk(imageUrl);snapshot = imageLoader.diskCache.get(key);if (snapshot == null) {//在磁盘中没有找到缓存文件,去网络下载,并写入到缓存中DiskLruCache.Editor editor = imageLoader.diskCache.edit(key);if (editor != null) {OutputStream outputStream = editor.newOutputStream(0); //因为是1对1,直接设为0,就是取第一个if (downloadUrlToStream(imageUrl, outputStream)) {editor.commit();}else {editor.abort(); //取消本次写入操作}}snapshot = imageLoader.diskCache.get(key);}if (snapshot != null) {inputStream = (FileInputStream) snapshot.getInputStream(0);descriptor = inputStream.getFD();}//将缓存数据解析成bitmap对象Bitmap bitmap = null;if (descriptor != null) {bitmap = BitmapUtil.decodeSampledBitmap(descriptor, reqWidth, reqHeight);}if (bitmap != null) {//将bitmap加入到内存缓存中imageLoader.addBitmapToMemoryCache(params[0], bitmap);}return bitmap;} catch (IOException e) {e.printStackTrace();}// 通过url下载图片Bitmap bitmap = downloadBitmap(params[0]);if (bitmap != null) {// 将图片放入内存缓存中imageLoader.addBitmapToMemoryCache(params[0], bitmap);}return bitmap;}
/** * 根据url从网上获取流,并写入到output流中 * @param imageUrl * @param outputStream * @return */private boolean downloadUrlToStream(String imageUrl,OutputStream outputStream){HttpURLConnection conn = null;BufferedOutputStream out = null;BufferedInputStream in = null;try {URL url = new URL(imageUrl);conn = (HttpURLConnection) url.openConnection();in = new BufferedInputStream(conn.getInputStream(), 8*1024);out = new BufferedOutputStream(outputStream, 8*1024);int b;while((b = in.read())!= -1){out.write(b);}return true;} catch (Exception e) {e.printStackTrace();}finally{if (conn != null) {conn.disconnect();}try {out.close();} catch (IOException e) {e.printStackTrace();}try {in.close();} catch (IOException e) {e.printStackTrace();}}return false;}大概步骤就是这样,中间一些细微的非核心代码没有放上来,可以直接去github上进行下载,包含示例demo
地址https://github.com/sheepm/Cache
0 0
- 自己写Android图片缓存框架之二级disk缓存
- 自己写Android图片缓存框架之一级内存缓存
- Android 二级图片缓存,图片优化,图片异步加载框架设计
- 教你写Android ImageLoader框架之图片缓存 (完结篇)
- Android之图片缓存
- Android之图片缓存
- 图片会说话系列之Android图片缓存框架
- Android图片缓存框架-Glide
- Android 图片缓存框架ImageLoader
- Android四大图片缓存框架之-Fresco(一)
- Android四大图片缓存框架之-Picasso和Glide
- Android开发之图片加载缓存框架Picasso的领教
- 自己写的缓存
- android图片缓存之softReference
- android 图片缓存之 createBitmap
- Android之图片缓存管理
- Android之缓存网络图片
- Android之图片缓存数据流
- mysql事务
- linux常用命令
- willMoveToParentViewController和didMoveToParentViewController
- 基于用户社会影响建模的情感识别系统框架(IEEE2014)
- 编程语言的几个命名法
- 自己写Android图片缓存框架之二级disk缓存
- 【Leetcode】Median of Two Sorted Arrays
- Linux学习记录(6)磁盘分区的基本操作
- WebStorm中SVN插件问题
- sql扫盲(2)
- 一个23岁大学生的Raspberry Pi开源项目
- UE4 Lightmass Global Illumination
- 用户空间与内核空间,进程上下文与中断上下文
- HDOJ 1028 Ignatius and the Princess III(DP)