《Android开发艺术探索》笔记——Bitmap的加载和Cache(二)
来源:互联网 发布:磁条卡写卡软件 编辑:程序博客网 时间:2024/05/21 17:05
上一篇记录了Bitmap的(高效)加载,那么这一篇就记录Cache。
对于网络上的图片,第一次使用就需要从网络上去下载下来,但如果每次都去从网络上下载,那就非常浪费流量了,所以需要做缓存。另外的添加了缓存也要做好删除缓存,毕竟有些过久地图片或是很少会再用到的图片,就需要删掉了,释放空间。
这里用到的缓存算法是LRU(Least Recently Used),最近最少使用算法。在该算法的基础上有衍生出两种缓存,LruCache和DiskLruCache,前者用于实现内存缓存,后者用于实现存储设备的缓存。所以这里就是将这两者结合,实现了一个ImageLoader(图片加载器),这里用到了三级缓存(网络缓存,磁盘缓存和内存缓存)。
1. LruCache
引用原文的话:
LruCache是一个泛型类,它内部采用一个LinkedHashMap以强引用的方式存储外界的缓存对象。
另外LruCache是线程安全的。
短短两句话涉及了不少概念。
LinkedHashMap如果去查找Lru算法的话,基本都是在它的基础上实现的;
从构造方法里可以看出它是个泛型类:
然后关于强引用:
之所以说线程安全,因为在LruCache里的添加,删除,获取都是有同步锁机制的。
1.1 LruCache的使用
int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);//单位KB //设定缓存的容量为总容量的1/8 int cacheSize = maxMemory / 8; mMemoryCache = new LruCache<String, Bitmap>(cacheSize) { @Override protected int sizeOf(String key, Bitmap value) { //返回bitmap大小的计算 return value.getRowBytes() * value.getHeight() / 1024; } };
总的来说就是提供缓存的总容量大小然后重写sizeof方法。这里缓存的总容量大小为当前进程的可用内存的1/8,sizeof里就返回的是bitmap对象的大小计算。这两个的单位应该一致,所以这里都除以1024。
然后是获取的方法:
mMemoryCache.get(key);
添加的方法:
mMemoryCache.put(key, bitmap);
2. DiskLruCache
2.1 DiskLruCache的使用
2.1.1 引用
用DiskLruCache来做磁盘缓存,可以通过依赖来获取:
compile 'com.jakewharton:disklrucache:2.0.2'
2.1.2 创建
DiskLruCache需要通过open方法来创建,而不是普通的构造方法:
//利用open方法来创建,第一个参数是存储路径,第二个参数是版本号,//第三个参数是单个节点对应的数据的个数,第四个参数是缓存的总大小 mDiskLruCache = DiskLruCache.open(diskCacheDir, 1, 1, DISK_CACHE_SIZE);
2.1.3 添加
与前面的LruCache一样,DiskLruCache也是用到了LinkedHashMap,那么在LruCache里的操作都用到了“key”这个东西,这里也同样用到了key。由于在这个ImageLoader里他们都是操作同一个东西,所以当然是一样的。作者在这里是用图片的url来作key,但需要作一些转换,用url的md5值来作为key,主要是防止url里可能有些特殊字符导致出错:
/** * 将图片的url转换成key,这里采用url的md5的值作为key * @param url * @return */ private String hashKeyFromUrl(String url) { String cacheKey; try { MessageDigest messageDigest = MessageDigest.getInstance("MD5"); messageDigest.update(url.getBytes()); cacheKey = bytesToHexString(messageDigest.digest()); } catch (NoSuchAlgorithmException e) { cacheKey = String.valueOf(url.hashCode()); } return cacheKey; }private String bytesToHexString(byte[] bytes) { StringBuilder stringBuilder = new StringBuilder(); for (int i = 0; i < bytes.length; i++) { String hex = Integer.toHexString(0xFF & bytes[i]); if (hex.length() == 1) { stringBuilder.append('0'); } stringBuilder.append(hex); } return stringBuilder.toString(); }
那么DiskLruCache的缓存添加是通过Editor来完成的,通过edit()方法和key就可以获取到这个Editor对象,进而可以获得文件输出流。
String key = hashKeyFromUrl(url); //使用Editor进行缓存添加的操作 DiskLruCache.Editor editor = mDiskLruCache.edit(key); if (editor != null) { OutputStream outputStream = editor.newOutputStream(DISK_CACHE_INDEX); }
那么怎么操作这个文件输出流呢?或者说它的数据从哪来呢?
其实它的数据是从它的更上一级,网络缓存那里来的,我们通过url去做网络请求的时候会获得一个输入流,然后我们把输入流写到这个输出流里,那么它就有数据了。
private boolean downloadUrlToStream(String urlString, OutputStream outputStream) { HttpURLConnection urlConnection = null; BufferedOutputStream out = null; BufferedInputStream in = null; try { URL url = new URL(urlString); urlConnection = (HttpURLConnection) url.openConnection(); in = new BufferedInputStream(urlConnection.getInputStream(), IO_BUFFER_SIZE); out = new BufferedOutputStream(outputStream, IO_BUFFER_SIZE); int b; while ((b = in.read()) != -1) { out.write(b); } return true; } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if (urlConnection != null) { urlConnection.disconnect(); } try { in.close(); out.close(); } catch (IOException e) { e.printStackTrace(); } } return false; }
现在我们通过网络请求将流写给了磁盘缓存,但需要通过进一步的确认操作来真正的写入。即commit()方法,所以把前面的一块代码修改下:
if (editor != null) { OutputStream outputStream = editor.newOutputStream(DISK_CACHE_INDEX); if (downloadUrlToStream(url, outputStream)) { editor.commit(); } else { editor.abort(); } mDiskLruCache.flush(); }
2.1.4 获取
DiskLruCache对缓存的获取则是通过它的Snapshot对象,与上面的类似,它是通过get()方法和key得到的,然后可以进一步的得到文件输入流,那拿到了文件输入流我们通过上一篇的BitmapFactory提供的解码方法就可以得到一个Bitmap对象了。
String key = hashKeyFromUrl(url); DiskLruCache.Snapshot snapshot = mDiskLruCache.get(key); if (snapshot != null) { //获取该图片的文件输入流 FileInputStream fileInputStream = (FileInputStream) snapshot.getInputStream(DISK_CACHE_INDEX); //获取该文件输入流的文件描述 FileDescriptor fileDescriptor = fileInputStream.getFD(); //通过文件描述得到想要的bitmap bitmap = mImageResizer.decodeSampleBitmapFromFileDescriptor(fileDescriptor, reqWidth, reqHeight); }
这里对文件输入流的处理是用了FileDescriptor, 也就是对应于decodeFileDescriptor()方法。为什么这里要用这个方法?
作者给的解释是FileInputStream是一种有序的文件流,两次decodeStream调用影响文件流的位置属性,在第二次decodeStream的时候会得到null,那这里我做了测试,确实在第二次的时候会得到null。
- 《Android开发艺术探索》笔记——Bitmap的加载和Cache(二)
- 《Android开发艺术探索》笔记——Bitmap的加载和Cache(一)
- 《Android开发艺术探索》笔记——Bitmap的加载和Cache(三)
- 《Android 开发艺术探索》随手笔记——第十二章Bitmap的加载和Cache
- 《android开发艺术探索》笔记之Bitmap的加载和Cache
- 《android开发艺术探索》笔记之Bitmap的加载和Cache
- 读书笔记-Android开发艺术探索-第12章-Bitmap的加载和Cache
- 《Android开发艺术探索》12章 Bitmap的加载和Cache
- Android开发艺术探索读书笔记(第12章 Bitmap的加载和Cache)
- 《Android开发艺术探索第十二章读书笔记》 Bitmap的加载和Cache
- Android开发艺术探索学习笔记--Bitmap的高效加载
- Bitmap的高效加载(Android开发艺术探索学习笔记)
- 《Android开发艺术探索》第十二章Bitmap加载和Cache小结
- Android开发艺术探索------Bitmap的高效加载
- Android——Bitmap的加载和Cache
- 《Android开发艺术探索》——笔记(一)
- ViewRoot,DecorView,MeasureSpec和View的工作原理——Android开发艺术探索笔记
- Android开发艺术探索笔记——第一章:Activity的生命周期和启动模式
- 实用类
- Dying[二分]
- HDU5873Football Games(思维题)
- LNMP1.2及之后版本–添加、删除虚拟主机教程
- adapter基类的封装
- 《Android开发艺术探索》笔记——Bitmap的加载和Cache(二)
- 简单瀑布流的实现
- 文件IO小项目-模拟登陆系统
- Google算法题:二进制手表
- bugku never give up
- 使用toggle()方法进行显示隐藏
- 集合(3)张飞
- 考察Hadoop的底层rpc通信(二)
- Java经典算法题(五)