1.Bitmap高效加载
加载Bitmap的方法:使用BitmapFactory的decodeFile/decodeResource/decodeStream/decodeByteArray可以分别从,文件/资源/输入流/字节数组中加载一个Bitmap。decodeFile/decodeResource会间接调用decodeStream。
通过采样率控制加载出的Bitmap的大小,以提高加载效率:
- 将BitmapFactory.Options的inJustDecodeBounds参数设置为true,并加载图片
- 从BitmapFactory.Options中取出图片的原始宽高信息,即outWidth和outHeight
- 根据采样率的规则(2的倍数)以及目标View的大小,计算出inSampleSize的大小
- 将BitmapFactory.Options的inJustDecodeBounds参数设置为false,重新加载图片
public static Bitmap decodeSampleBitmapFromResource(Resources resource, int resId,
int reqWidth, int reqHeight) {
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(resource, resId, options);
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(resource, resId, options);
}
public static int calculateInSampleSize (BitmapFactory.Options options, int reqWidth, int reqHeight) {
int width = options.outWidth;
int height = options.outHeight;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
int halfWidth = width / 2;
int halfHeight = height / 2;
while (halfHeight / inSampleSize >= reqHeight
&& halfWidth / inSampleSize >= reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
2.Android中的缓存策略
LRU(Least Recently Used):内存缓存LruCache和存储设备缓存DiskLruCache
- LruCache
内部采用一个LinkedHashMap,以强引用的方式存储外界的缓存对象。使用方法,设置缓存大小,然后复写sizeOf计算缓存对象的大小,之后通过put和get方法,添加和获取缓存对象:
int cacheSize = 4 * 1024 * 1024; // 4MiB
LruCache<String, Bitmap> bitmapCache = new LruCache<String, Bitmap>(cacheSize) {
protected int sizeOf(String key, Bitmap value) {
return value.getByteCount();
}
}}
如果缓存对象引用的资源需要显式的进行释放,可以复写entryRemoved方法释放资源。
当通过get方法成功访问到一个缓存对象,在LruCache内部会将其移动到队列头部;通过put方法添加缓存时,如果缓存已满,队列最后一个对象就会被删除。可以通过remove方法直接删除一个缓存对象。
LruCache是线程安全的。
2. DiskLruCache
将缓存对象写入文件系统进行缓存。不在sdk中,需另行下载。源码
- 创建过程
通过DiskLruCache#open方法进行
public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize)
throws IOException {
参数:
- directory:缓存目录
- appVersion:应用版本号,版本号改变时会清空之前的缓存
- valueCount:单个key对应的数据个数
- maxSize:缓存空间大小
private static final long DISK_CACHE_SIZE = 1024 * 1024 * 50;
File diskCacheDir = getDiskCacheDir(mContext, "bitmap");
if (!diskCacheDir.exists()) {
diskCacheDir.mkdirs();
}
mDiskLruCache = DiskLruCache.open(diskCacheDir, 1, 1, DISK_CACHE_SIZE);
- 添加缓存
DiskLruCache的缓存添加操作,通过Editor来完成。通过Editor得到一个输出流:
DiskLruCache.Editor editor = mDiskLruCache.edit(key);
if (editor != null) {
OutputStream out = editor.newOutputStream(0); // valueCount为1,index从0开始
}
将数据写入到该输出流之后,调用editor的commit方法,缓存创建完成:
if (downloadUrlToStream(url, outputStream)) {
editor.commit();
}
- 获取缓存
DiskLruCache的get方法获取一个Snapshot对象,通过它便可得到输入流,通过该输入流便可得到缓存的文件。对于缓存Bitmap时的处理:因为在加载大图的时候,需要decode两次,而输入流读取过一次,第二次就会返回null,对于这种情况,可以通过输入流获得文件的文件描述,然后调用BitmapFactory.decodeFileDescriptor来加载Bitmap:
DiskLruCache.Snapshot snapshot = mDiskLruCache.get(key);
if (snapshot != null) {
FileInputStream in = (FileInputStream) snapshot.getInputStream(0);
FileDescriptor fd = in.getFD();
bitmap = decodeSampleBitmapFromFD(fd, reqWidth, reqHeight);
if (bitmap != null) {
addBitmapToMemoryCache(key, bitmap);
}
}