Bitmap的高效加载和 Cache
Posted Young_xiaoT
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Bitmap的高效加载和 Cache相关的知识,希望对你有一定的参考价值。
Bitmap的高效加载和 Cache
Bitmap 的高效加载
如何加载一个图片呢?BitmapFactory 类提供了四类方法:
- decodeFile 文件
- decodeResource 资源
- decodeStream 输入流
- decodeByteArray 字节数组
可以采用 BitmapFactory.Options 来加载所需尺寸的图片,这样就可以按照一定的采样率来加载缩小后的图片,这样可以较低内存占有从而在一定程度上避免 OOM,提高 Bitmap 加载时的性能。
通过 BitmapFactory.Options 来缩放图片,主要用到它的 inSampleSize 参数,即采样率。
当inSampleSize = 1 时,采样后的图片大小为图片原始大小;当 inSampleSize 大小等于2时,图片宽高均为原来的 1/2 ,像素数为原图的 1/4 ,内存也为原图的 1/4.
如何获取采样率呢?可以遵循如下的规则:
- 将 BitmapFactory.Options 的 inJustDecodeBounds 参数设置为 true 并加载图片。
- 从 BitmapFactory.Options 中获取图片的原始宽高信息,他们对应于 outWidth 和 outHeight 参数。
- 根据采样率规则并结合目标 View 的所需大小计算出采样率 inSampleSize。
- 将 BitmapFactory.Options 的 inJustDecodeBounds 参数设置为 false,然后重新加载图片。
注:inJustDecodeBounds设为 true时,BitmapFactory 只会解析图片的原始宽/高信息,并不会正在去加载图片,所以这个操作是轻量级的。
android 中的缓存策略
目前常用的的一中缓存算法是 LRU(Least Recently Used),近期最少使用算法,它的核心思想就是当缓存满时,会优先删除那些近期最少使用的缓存对象。
采用 LRU 算法的缓存有两种: LruCache 和 DiskLruCache,LruCache 用于实现内存缓存,而 DiskLruCache 用于存储设备缓存。
LruCache
LruCache 是一个泛型类,它内部采用一个 LinkedHashMap 以强引用的方式存储外界的缓存对象,其提供了 get 和 put 方法来完成缓存的获取和添加操作,当缓存满时,LruCache 会移除较早使用的缓存对象,然后再添加新的缓存对象。
- 强引用:直接对象的引用;
- 软引用:当一个对象只用软引用存在时,系统内存不足时此对象会被 gc 回收;
- 弱引用:当一个对象只用弱引用存在时,此对象会随时被 gc 回收;
另外 LruCache 是线程安全的:
public class LruCache<K, V>
private final LinkedHashMap<K, V> map;
...
LruCache 典型初始化过程:
int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
int cacheSize = maxMemory / 8;
mMemoryCache = new LruCache<String, Bitmap>(cacheSize)
@Override
protected int sizeOf(String key, Bitmap value)
return value.getRowBytes() * value.getHeight() / 1024;
;
这里只需要提供缓存的总容量大小(一般为进程可用内存的1/8)并重写 sizeOf 方法即可.sizeOf方法作用是计算缓存对象的大小。
还有获取和添加方法,都比较简单:
mMemoryCache.get(key)
mMemoryCache.put(key,bitmap)
DiskLruCache
DiskLruCache 用于实现存储设备缓存,即磁盘缓存,它通过将缓存对象写入文件系统从而实现缓存的效果。
1. DiskLruCache 的创建
它提供了 open 方法用于创建自身:
public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize)
- 第一个参数表示磁盘缓存在文件系统中额存储路劲。
- 第二个参数为版本号,一般为1即可。
- 第三个参数表示单个节点所对应的数据的个数,一般为1即可
- 第四个参数表示缓存的总大小
2. DiskLruCache 的缓存添加
DiskLruCache 的缓存添加的操作是通过 Editor 完成的,Editor 表示一个缓存对象的编辑对象。
首先获取图片 url 所对应的 key,然后根据 key 通过 editor() 来获取 Editor 对象,如果这个缓存正在被编辑,那么 editor()会返回 null,即 DiskLruCache 不允许同时编辑一个缓存对象。
private String hashKeyFromUrl(String url)
String cacheKey;
try
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
messageDigest.update(url.getBytes());
cacheKey = bytesToHexString(messageDigest.digest());
catch (NoSuchAlgorithmException e)
e.printStackTrace();
return null;
private String bytesToHexString(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();
将图片的 url 转成 key 之后,就可以获取 Editor 对象了。
String key = hashKeyFromUrl(url);
DiskLruCache.Editor editor = mDiskLruCache.edit(key);
if (editor == null)
OutputStream outputStream = editor.newOutputStream(DISK_CACHE_INDEX);
有了文件输出流,当从网络下载图片时,图片就可以通过这个文件输出流写入到文件系统:
public boolean downloadUrlToStream(String urlString,
OutputStream outputStream)
HttpURLConnection urlConnection = null;
BufferedOutputStream out = null;
BufferedInputStream in = null;
try
final 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 (IOException e)
Log.e(TAG, "downloadBitmap failed." + e);
finally
if (urlConnection != null)
urlConnection.disconnect();
MyUtils.close(out);
MyUtils.close(in);
return false;
经过上面的步骤,其实并没有真正地将图片写入文件系统,还必须通过 Editor 的 commit() 来提交写入操作:
DiskLruCache.Editor editor = mDiskLruCache.edit(key);
if (editor != null)
OutputStream outputStream = editor.newOutputStream(DISK_CACHE_INDEX);
if (downloadUrlToStream(url, outputStream))
editor.commit();
else
editor.abort();
mDiskLruCache.flush();
3. DiskLruCache 的缓存查找
缓存查找也需要将 url 转换成 key,然后通过 DiskLruCache 的 get 方法得到一个 Snapshot 对象,接着通过 Snapshot 对象即可得到缓存的文件输入流,有了文件输入流,就可以得到 Bitmap 对象。
String key = hashKeyFormUrl(url);
DiskLruCache.Snapshot snapShot = mDiskLruCache.get(key);
if (snapShot != null)
FileInputStream fileInputStream = (FileInputStream)snapShot.getInputStream(DISK_CACHE_INDEX);
FileDescriptor fileDescriptor = fileInputStream.getFD();
bitmap = mImageResizer.decodeSampledBitmapFromFileDescriptor(fileDescriptor,
reqWidth, reqHeight);
if (bitmap != null)
addBitmapToMemoryCache(key, bitmap);
打完收工!
以上是关于Bitmap的高效加载和 Cache的主要内容,如果未能解决你的问题,请参考以下文章