加载Bitmap主要使用的是Android系统提供的BitmapFactory,提供了四种方法,decodeFile、decodeResource、decodeStream、decodeByteArray。
高效加载Bitmap就是利用BitmapFactory.Options来加载所需要的尺寸图片,利用采样率参数inSampleSize,当inSampleSize为1是,采样后的图片为原始大小,当采样率为2时,宽高都为原来的1/2,像素数为原来的1/4,内存大小为原来的1/4。imSampleSize小于1,无缩放效果。
加载图片步骤:
将BitmapFactory.Options的inJustDecodeBounds参数设为true,并加载图片,就可以得到BitmpaFactory.Options里图片的原始宽高信息,对应于outWidth和outHeight,根据需要的图片大小和原始宽高计算采样与,再将BitmapFactory.Options.inJustDecodeBounds设置为false重新加载图片就可以得到目标图片的大小。
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
public static int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
//图片实际大小每次对半减,直到长和宽同时小于等于我们需要的长和宽
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) > reqHeight
&& (halfWidth / inSampleSize) > reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
常用的缓存算法是LRU(Least Recently Used),最近最少使用算法,当缓存满是,优先淘汰最近最少使用的缓存对象。Android中主要用LruCache实现内存缓存,DiskLruCache实现磁盘缓存。
强应用:直接的对象引用
软引用:当一个对象只有软引用存在是,系统内存不足时此对象就会被gc回收
弱引用:当一个对象只有弱引用是,此对象随时被gc回收
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) {
return bitmap.getRowBytes() * bitmap.getHeight() / 1024;
}
protected void entryRemoved(boolean evicted, K key, V oldValue, V newValue) {
//do something
}
}
//获取缓存对象
mLruCache.get(key);
//存储缓存对象
mLruCache.put(key,bitmap);
//移除缓存对象
mLruCache.remote(key);
sizeOf用于计算缓存对象大小,这里的大小单位和总容量一直,这里是KB。LruCache源码。
1.创建缓存
public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize)
directory:可写的缓存目录,可以使用SD卡上面的缓存目录/sdcard/Android/data/package_name/cache,使用该目录的好处就是当APK卸载之后,就会自动删除目录
valueCount:单个节点所对应的缓存个数,一般为1即可
maxSize:缓存大小,单位字节
2.添加缓存
DiskLruCache.Editor editor = mDiskLruCache.editor(key);
OutputStream os = editor.newOutputStream(0);
if(downloadUrlToStream(url,os)) {
editor.commit();
}else {
editor.abort();
}
mDiskLruCache.flush();
3.缓存查找
DiskLruCache.Snapshot snapShot = mDiskLruCache.get(key);
if(snapShot != null) {
FileInputStream fis = (FileInputStream)snapShot.getInputStrean(0);
FileDescriptor fd = fis.getFD();
//...
}
FileInputStream是一种有序的文件流,decodeStream会影响文件流的属性,导致第二次decodeStream为空,可以通过BitmapFactory.decodeFileDescriptor方法来加载一张缩放后的图片。
4.删除缓存
一个优秀的ImageLoader应该具备的功能:
文中的例子使用线程池和Handler来实现的,示例代码。
列表滑动的时候停止加载图片,停止下来在加载图片,可以给ListView或者GridView设置setOnScrillListener,在OnScrollListener的onScrollStateChanged方法中判断列表是否处于滑动状态。