Bitmap的加载和Cache
Bitmap的高效加载
Bitmap 在Android中指的是一张照片,可以是Png格式也可以是Jpg等其他常见的图片格式。那么如何加载一个图片呢?
BitmapFactory类提供了四种方法:
- decodeFile 从文件系统
- decodeResource 从资源
- decodeStream 从输入流
- decodeByteArray 从字节数组
其中decodeFile 和 decodeResource又间接调用了decodeStream方法,这四种方法最终是在Android的底层实现的。对应着BitmapFactory类中的几个native方法。
高效加载Bitmap的实现 其核心思想很简单:
通过BitmapFactory.Options 使用其 inSampleSize(采样率)来缩放大图片。通过inSampleSize缩放后可以降低内存的占用从而在一定程度上避免OOM(Out Of Memory Error),提高Bitmap加载时的性能。
附上一段代码(Button按钮点击,ImageView加载资源图片 手机:小米NOTE Pro)
1 | @Override |
Android的缓存策略
缓存策略在Android中有着广泛的使用场景。但为了避免下载图片消耗过多的流量,缓存策略此时就变的很重要。当程序第一次从网络加载图片后,将其缓存到存储设备上,下次使用的时候就不必再从网络拉取,为了提高用户体验,往往还会降图片再在内存中缓存一份,这样当应用打算从网络请求一张图片的时候,会先从内存中获取,内存没有就从存储设备获取,存储设备没有就在从网络上拉取。
LRU算法(Least Recently Used):近期最少使用算法。其核心思想是当缓存满时,会优先淘汰那些近期最少使用的缓存对象。
LRU算法缓存有两种:LruCache(内存缓存) 和 DishLruCache(存储设备缓存),通过二者的完美结合,就能实现一个具有很高使用价值的ImageLoader。
内存缓存LruCache
LruCache 是一个线程安全的广泛类,其内部采用一个LinkedHashMap以强引用的方式存储外界的缓存对象,提供了get和put方法来完成缓存的获取和添加操作,当缓存满时,LruCache会移除较早使用的缓存对象,然后再添加新的缓存对象。
- 强引用:直接的对象引用;
- 软引用:当一个对象只有软引用存在时,系统内存不足时此对象会被gc回收;
- 弱引用:当一个对象只有弱引用存在时,此对象会随时被gc回收;
LruCache的定义:
1 | public class LruCache<K,V>{ |
- LruCache的实现也很简单,附上LruCache的典型初始化过程:
1 | int maxMemory = (int) Runtime.getRuntime().maxMemory(); |
- LruCache的添加和获得一个缓存对象也很简单:
1 | mMemoryCache.get(key); // 获得缓存 |
存储设备缓存DiskLruCache
DiskLruCache 磁盘缓存:通过将缓存对象写入文件系统从而实现缓存的效果。
DiskLruCache得到了Android官方文档的推荐,但不属于AndroidSDK的一部分,源码可从如下网址得到
1 | https://android.googlesource.com/platform/libcore/+/android-4.1.1_r1/luni/src/main/java/libcore/io/DiskLruCache.java |
- DiskLruCache的创建
1 | private static final long DISK_CACHE_SIZE = 1024 * 1024 * 50;//磁盘缓存50M大小 |
- DiskLruCache的缓存添加
DiskLruCache缓存添加是通过Editor完成的。当使用Editor编辑完缓存对象后,记得使用commit().
注意:图片的url可能含有特殊字符,所以一般采用url的md5值做为key。
1 | String imgUrl = ""; |
- DiskLruCache的缓存查找
通过DiskLruCache.get获取一个SnapShot对象,再使用Snapshot对象即可获得缓存的文件输入流。
关于FileInputStream(文件描述符):直接使用BitmapFactory.Options 对FileInputStream进行缩放会出现问题,因为FileInputStream是一种有序的文件流,两次decodeStream调用会影响文件流的位置属性,导致第二次decodeStream时得到的是null。为了解决这个问题,可以通过文件流来得到它所对应的文件描述符,然后通过BitmapFactory.decodeFileDescriptor来加载一张缩放后的图片。
附上一段代码:
1 | String key = MD5Util.getMd5Value(imgUrl); |