Bitmap的加载和Cache Bitmap的高效加载 使用BitmapFactory
加载一张图片的方式
decodeFile
从文件
decodeResource
从资源
decodeStream
从输入流
decodeByteArray
从字节数组核心思想
采用BitmapFactory.Options
按照一定的采样率(inSampleSize
)来加载缩小后的图片,这样就会降低内存的占用避免OOM
,提高了Bitmap加载时的性能inSampleSize = 1
,那么采样后的图片是原始图片的大小。inSampleSize大于1等于2时,采样后的图片的宽高均为原图的二分之一,像素数为原图的四分之一
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 //获取采样率 public static Bitmap decodeFile(String path) { int finalWidth = 800;//规定宽度如果为800 //先获取宽度 BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true;//图片不加载到内存 BitmapFactory.decodeFile(path,options); //取出图片的原始宽度 int outWidth = options.outWidth; int inSampleSize = 1; if (outWidth>finalWidth){ inSampleSize = outWidth/finalWidth; } //设置回去 options.inSampleSize = inSampleSize; options.inJustDecodeBounds = false; return BitmapFactory.decodeFile(path,options); }
Android中的缓存策略
避免过多的流量消耗需要进行缓存,当程序第一次从网络加载图片后,将其缓存到存储设备上,下次使用的时候就不必再从网络拉取,为了提高用户体验,往往还会降图片再在内存中缓存一份,这样当应用打算从网络请求一张图片的时候,会先从内存中获取,内存没有就从存储设备获取,存储设备没有就在从网络上拉取。
缓存策略LRU(Least Recently Userd)
算法: 最近最少使用算法,当缓存满时,会优先淘汰那些近期最少使用的缓存对象。LruCache
用来实现内存缓存,LruDiskCache
用来实现存储设备缓存。二者可以完美结合。
LruCache
LruCache
: 是一个泛型类,是线程安全的。内部采用LinkedHashMap
以强引用的方式存储外界的缓存对象,提供了get和put方法来完成缓存的获取和添加操作,当缓存满时,LruCache会移除较早使用的缓存对象,然后再添加新的缓存对象。
强引用: 直接的对象引用;
软引用: 当一个对象只有软引用存在的时候,系统内存不足的时候,对象会被回收;
弱引用: 当一个对象只有弱引用存在的时候,对象随时可能被系统回收。
1 2 public class LruCache<K, V> { private final LinkedHashMap<K, V> map;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 // LruCache 内存缓存 初始化操作 int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); int cacheSize = maxMemory / 8;//总容量的大小为当前最大内存的八分之一 mLruCache = new LruCache<String, Bitmap>(cacheSize) { //计算缓存对象的大小 @Override protected int sizeOf(String key, Bitmap bitmap) { //注意单位的转换 bytes --> kb return bitmap.getRowBytes() * bitmap.getHeight() / 1024; } //移除旧缓存时调用 @Override protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue) { super.entryRemoved(evicted, key, oldValue, newValue); // TODO: 2017/9/1 资源回收的工作 } };
DiskLruCache
DiskLruCache:
磁盘缓存,通过将缓存对象写入文件系统从而实现缓存效果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 private static final long DISK_CACHE_SIZE = 1024 * 1024 * 50;//磁盘缓存50M大小 File diskCacheDir = getExternalCacheDir(); if (!diskCacheDir.exists()){ diskCacheDir.mkdirs(); } /** * Opens the cache in {@code directory}, creating a cache if none exists * there. * * @param directory a writable directory 缓存目录 * @param appVersion 应用版本号 * @param valueCount the number of values per cache entry. Must be positive. * @param maxSize the maximum number of bytes this cache should use to store * @throws IOException if reading or writing the cache directory fails */ DiskLruCache diskLruCache = DiskLruCache.open(diskCacheDir, 1, 1, DISK_CACHE_SIZE);
DiskLruCache的缓存添加DiskLruCache.Editor: 表示一个缓存对象的编辑对象。最后记得commit()
注意: 因为图片的url可能含有特殊字符,所以一般采用url
的md5
值做为key
。
1 2 3 4 5 6 7 8 9 String imgUrl = ""; String key = MD5Util.getMd5Value(imgUrl); DiskLruCache.Editor editor = diskLruCache.edit(key); if (editor != null) { editor.getString(0); } editor.commit();//提交 editor.abort();//回退 diskLruCache.flush();//刷新
DiskLruCache的缓存查找
通过DiskLruCache.get获取一个SnapShot对象,再使用Snapshot对象即可获得缓存的文件输入流。
1 2 3 4 5 6 7 8 9 10 11 12 String key = MD5Util.getMd5Value(imgUrl); Bitmap bitmap = null; DiskLruCache.Snapshot snapshot = diskLruCache.get(key); if (snapshot != null) { FileInputStream fileInputStream = (FileInputStream) snapshot.getInputStream(DISK_CACHE_INDEX); //从文件流中获取文件描述符 FileDescriptor fileDescriptor = fileInputStream.getFD(); bitmap = decodeSampledBitmapFromFileDescriptor(fileDescriptor,reqWidth,reqHeight); if (bitmap != null) { addBitmapToMemoryCache(key,bitmap); } }
FileDescriptor:文件描述符
直接使用BitmapFactory.Options
对FileInputStream
进行缩放会出现问题,因为FileInputStream
是一种有序的文件流,两次decodeStream
调用会影响文件流的位置属性,导致第二次decodeStream
时得到的是null
。为了解决这个问题,可以通过文件流来得到它所对应的文件描述符,然后通过BitmapFactory.decodeFileDescriptor
来加载一张缩放后的图片。
DiskLruCache.delete
DiskLruCache.remove