一.Bitmap的高效加载
Bitmap:Bitmap在Android中指的是一张图片,可以实png格式也可以是jpg等其他常见的图片格式。
高效加载的核心思想:高效加载Bitmap主要是使用BitmapFactory.Options来加载所需尺寸的图片。其主要原理就是通过BitmapFactory.Options就可以按一定的采样率来加载缩小后的图片,将缩小后的图片在ImageView中显示,这样就会降低内存占用从而在一定程度上避免OOM。
高效加载过程:只要获得了BitmapFactory.Options的inSampleSize参数,即可以进行高效的加载,其过程如下:
inSampleSize即采样率,当inSampleSize的值为1时,表示采样后的图片大小为图片的原始大小;当inSampleSize的值为2时,则表示采样后的图片大小为原始图片的1/2,而像素数为原来的1/4,以此类推。需要注意的是当inSampleSize被赋予小于1时,则会被系统默认当作1来进行处理,并且inSampleSize在官方建议中应该是为2的指数。
(1)将BitmapFactory.Options的inJustDecodeBounds的参数设置true并加载图片。
(2)从BitmapFactory.Options中取出图片的原始宽高信息,他们对应于outWidth和outHeight参数。
(3)根据采样率的规则并结合目标View所需大小计算出采样率。
(4)将BitmapFactory.Options的inJustDecodeBounds参数设为false,然后重新加载图片。
上述4个过程用代码实现如下:
public static Bitmap decodeSampleFromResource(Resource res,int resId,int reqWidth,int reqHeight){
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true; //设置为true
BitmapFactory.decodeResource(res,resId,options);
options.inSampleSize = calculateInSampleSize(options,reqWidth,reqHeight);
return BitmapFactory.decodeResource(res,resId,options);
}
public static int calculateInSampleSize(BitmapFactory.Options options,int reqWidth,int reqHeight){
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;
while((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth){
inSampleSize = inSampleSize*2; //类似于递归
}
}
return inSampleSize;
}
mImageView.setImageBitmap( //对ImageView使用
decodeSampledBitmapFromResource(getResources(),R.id.myImage,100,100)
);
二.Android中的缓存策略
缓存的概括:缓存,即当程序第一次从网上加载资源后,就将其缓存到存储设备上,这样下次再使用这张图片就不用再从网络上获取了。缓存策略一般包含缓存的添加、获取和删除这三类操作。目前最常用的缓存算法是LRU算法,基于该算法的缓存有两种:LruCache(内缓存)和DiskLruCache(存储设备缓存)。
1.LruCache
概念:LruCache是一个泛型类,它内部采用一个LinkedHashMap以强引用的方式存储外界的缓存对象,其提供了get和put方法来完成缓存的获取和添加操作。当缓存满时,LruCache会首先移除较早的缓存对象,再将新的缓存加入进去。LruCache是线程安全的。
强引用:直接的对象引用。
软引用:当一个对象只有软引用存在时,系统内存不足时此对象会被gc回收。
弱引用:当一个对象只有弱引用存在时,此对象随时会被gc回收。
public class LruCache<K,V>{ //构造方法
private final LinkedHashMap<K,V> map;
}
LruCache的使用:
(1)初始化
int maxMemory = (int)(Runtime.getRuntime().maxMemory()/1024);
int cacheSize = maxMemory/8;
mMemoryCache = new LruCache<String,Bitmap>(cacheSize){
@Override
protected int sizeOf(String key,Bitmap bitmap){ //计算缓存对象的大小
return bitmap.getRowBytes()* bitmap.getHeight()/1024; //转换单位
}
};
(2)获取缓存对象
mMemoryCache.get(key)
(3)添加一个缓存对象
mMemoryCache.put(key,bitmap)
2.DiskLruCache
DiskLruCache的创建:DiskLruCache并不能通过构造方法来创建,它提供了open方法用于创建自身:
public static DiskLruCache open(File directory,int appVersion,int valueCount,long maxSize)
/*第一个参数:磁盘缓存在文件系统中的存储路径
第二个参数:应用版本号
第三个参数:单个节点所对应的数据的个数,默认值为1
第四个参数:缓存的总大小,超过这个大小就会自动清除一些缓存*/
DiskLruCache的典型创建过程如下:
private static final long DISK_CACHE_SIZE=1024*1024*50; //50MB
File diskCacheDir = getDiskCacheDir(mContext,"bitmap");
if(!diskCacheDir.exists()){
diskCacheDir.mkdirs();
}
mDiskLruCache = DiskLruCache.open(diskCacheDir,1,1,DISK_CACHE_SIZE);
DiskLruCache的缓存添加:往DiskLruCache中添加缓存,首先是需要获取到图片url所对应的key:
private String hashKeyFormUrl(String url){
String cacheKey;
try{
final MessageDigest mDigest = MessageDigest.getInstance("MD5"); //将url中的md5的值作为key
mDigest.update(url.getBytes());
cacheKey = bytesToHexString(mDigest.digest());
}cache(NoSuchAlgorithmException e){
cacheKey = String.valueOf(url.hashCode());
}
return cacheKey;
}
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 = hashKeyFormUrl(url);
DiskLruCache.Editor editor = mDiskLruCache.edit(key);
if(editor != null){
OutputStream outputStream = editor.newOutputStream(DISK_CACHE_INDEX); //得到一个文件输出流
}
在获得了文件输出流之后,我们就可以通过这个文件输出流将文件写入到文件系统当中,最后只需要通过Editor的commit()来提交写入操作,如果这个过程发生了异常,可以通过Editor的abrot()来回退整个操作:
OutputStream outputStream = editor.newOutputStream(DISK_CACHE_INDEX);
if(downloadUrlToStream(url,outputStream)){ //没发生异常,则直接提交
editor.commit();
}
else{ //发生异常,进行回退
editor.abrot();
}
mDiskLruCache.flush();
DiskLruCache的缓存查找:缓存查找也需要将url转换为key,然后通过DiskLruCache的get方法得到一个Snapshot对象,通过该对象即可得到缓存的文件输入流,之后就可以根据文件输入流得到Bitmap对象了:
Bitmap bitmap = null;
String key = hashKeyFormUrl(url);
DiskLruCache.Snapshot snapShot = mDiskLruCache.get(key);
if(DiskLruCache != null){
FileInputStream fileInPutStream = (FileInputStream)snapShot.getInputStream(DISK_CACHE_INDEX);
FileDescriptor fileDescriptor = fileInPutStream.getFD();
bitmap = mImageResizer.decodeSampledBitmapFromFileDescriptior(fileDescriptor,reqWidth,reqHeight);
if(bitmap != null){
addBitmapToMemoryCache(key,bitmap);
}
}
在上述代码中,我们需要注意的是在得到图片(文件)的输入流之后,一般不会直接在家原始的图片,而是通过BitmapFactory.decodeFileDescriptor的方法来对图片进行缩放加载。
网友评论