Bitmap在Android中指的是一张图片,可以是png格式和jpg格式,
Bitmap加载图片的方法,有BitmapFactory提供了 decodeFile、decodeResource、decodeStream、decoceByteArray,分别用于支持从文件系统、资源、输入刘、字节数组中加载一个Bitmap对象
如何高效地加载Bitmap
采用BitmapFactory.Options来加载所需尺寸的图片,通过BitmapFactory.Options可以按照一定的采样率来加载缩小后的图片。BitmapFactory提供的加载图片的四类方法都支持BitmapFactory,Options参数。通过BitmapFactory.Options来缩放图片主要是用到了他的inSampleSize参数,当inSampleSize为1时,采样后的图片大小为图片的原始大小。为2时,采样后图片的宽高均为原始图片的1/2,而像素为原图的1/4。采样率inSampleSize必须是大于1的整数才有缩小效果,如果小于1,其作用相当于1,另外最新的官方文档指出,inSampleSize的取值应该是为2的指数
获取采样率
(1)将BitmapFactory.Options的inJustDecodeBounds参数设为true并加载图片
(2)从BitmapFactory.Options中取出图片的原始宽高信息,他们对应于outWidth和outHeigth参数
(3)根据采样率的规则并结合目标View的所需大小计算出采样率inSampleSize
(4)将BitmapFactory.Option的inJustDecodeBounds参数设为false
inJustDecodeBounds参数,当此参数设为true是,BitmapFactory只会解析图片的原始宽高信息,并不会真正的加载图片
注意,这个时候BitmapFactory获取图片的宽高信息和图片的位置以及程序运行的设备有关
eg
public static Bitmap decodeSampledBitmapFromResource(Resource res ,int resId,int reqWidth,int reHeight){
final BitmapFactory.Options options = new BitmapFactory.Option();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res,resId,option);
options.inSampleSize = calculateInSampleSize(Options,reqWidth,resHeigth);
options.inJustDecodeBounds = false;
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 = optionts.outWidth;
int inSampelSize = 1;
if(heigth>reqHeigth || width > reqWidth){
final int halfHeigth = heigth/2;
final int halfWidth = width/2;
while((halfHeigth/inSampleSize) >= reqHeigth && (halfWidth/inSampleSize) >= reqWidth){
inSampleSize *=2;
}
}
return inSampleSize;
}
Android中的缓存策略
LurCache
LurCache是一个泛型类,它内部采用一个LinkedHashMap以强引用的方式存储外界的缓存对象,其提供了get和put方法来完成缓的获取和添加操作,当缓存满时,LurCache会移除较早使用的缓存对象,然后再添加新的缓存对象
eg
创建LurCache
int maxMemory = (int) (Runntime.getRuntime().maxMemory()/1024);
int cacheSize = maxMemory/8;
mMemory = new LurCache<String,Bitmap>(cacheSize){
@override
protected int sizeOf(String key, Bitmap bitmap){//计算缓存对象的大小
return bitmap.getRowBytes()*bitmap.getHeigth()/1024;
}
};
一些特殊的情况,还需要重写LurCache的entryRemoved方法,LurCache移除旧缓存时会调用entryRemoved方法,因此可以在该方法中完成一些资源回收工作
从LurCache中获取缓存对象
mMemory.get(key)
向LurCache添加对象
mMemory.put(key,bitmap)
DisLurCache
DisLurCache用于实现设备缓存,即磁盘缓存
DisLurCache的创建
public static DisLurCache open(File directory,int appVersion,int valueCount, long maxSize)
参数
directory:磁盘缓存的文件系统中的存储路径。缓存路径可以选择SD卡上的缓存目录,具体指/sdcard/Android/data/package_name/cache目录,其中package_name表示当前应用的包名,当应用被卸载后,此目录会一并被删除,当然也可以选择SD卡上的其他指定目录,建议,如果应用卸载后就希望删除缓存文件,那么就选择SD卡上的缓存目录
appVersion:表示应用的版本号,一般设置为1,当版本好发生改变时DisLurCache会清空之前的所有的缓存文件
valueCount:表示单个节点岁对应的数据个数,一般设置为1
maxSize:表示缓存的总大小,当缓存大小超出这个设定值后,DisLurCache会清除一些缓存从而保证总大小不大于这个设定值
eg
创建过程
private static final long DISK_CACHE_SIZE = 1024*1024*50;
File dislCacheDir = getDiskCacheDir(mContext,"bitmap");
if(!disCacheDir.exist()){
diskCacheDir.mKdirs();
}
mDiskLruCache = DiskLruCache.open(disCacheDir,1,1,DISK_CACHE_SIZE );
DiskLruCache的缓存添加
DiskLruCache的缓存添加的操作是通过Editor完成的,Editor表示一个缓存对象的编辑对象,这个用图片缓存举例,首先需要获取图片url所对应的key,然后根据Key就可以通过edit()来获取Editor对象,如果缓存对象正在被编辑,那么edit()会返回null,即DiskLruCache不允许同时编辑一个缓存对象
private String hashKeyFromFormUrl(String url){
String cacheKey;
try{
final MessageDigset mDigest = MessageDigest.getInstance("MD5");
mDigest.update(url.getBytes());
cacheKey = bytesToHexString(mDigest.digest());
}catch(NoSuchAlgorithmException){
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();
}
String key = hashKeyFormUrl(url);
DiskLruCache.Editor editor = mDiskLruCache.edit(key);
if(editor != null){
OutputStream outputStream = editor.new OutputStream(DISK_CACHE_INDEX);
}
public boolean downloadUrlToStream(String urlString,OutputStream outputStrean){
HttpUrlConnect urlConnect = null;
BufferedOutputStream out = null;
BufferedInputStream in = null;
try{
final URL url = new Url(urlStriing);
urlConnect = (HttpUrlConnect )tpurl.openConnect();
in = new BufferedInputStream(urlConnect.getInputStream,IN_BUFFER_SIZE);
out = new BufferedOutputStream(outputStream,IN_BUFFER_SIZE);
int b;
while((b = in.read()) != -1){
out.write(b);
}
return true;
}catch(IOException e){
}finally{
if(urlConnect != null){
urlConnect.disconnect();
}
}
reture false;
}
OutputStream outputStream = editor.newOutputStream(DISL_CACHE_INDEX);
if(downloadUrlToStream(url,outputStream)){
editor.commit();
}else{
editor.abort();
}
mDiskLruCache.flush();
DiskLruCache的查找
使用BitmapFactory.Options对像加载缩放图,这种方法对FileInputStream的缩放存在问题,原因是FileInputStream是一种有序的文件流,而两次decodeStream调用与影响了文件流的位置属性,导致第二次decodeStream时得到的是null;解决方法,,可以通过文件流得到所对应的文件描述符,然后再通过BitmapFactory.decodeFileDescriptor方法
来加载一张缩放后的图片。
Bitmap bitmap = null;
String key = hashKeyFormUrl(url);
DiskLruCache.Snapshot snapshot = mDiskLruCache.get(key);s
if(snapshort != null){
FileInputStream fileInputStream = (FileInputStream )snapshot.getInputStream(DISK_CACHE_INDEX);
FileDescriptor fileDescriptor = fileInputStream.getFD();
bitmap = mImageResizer.decodeSampledBitmapFrimFileDescriptor(fileDescriptor,reqWidth,reqHeigth);
if(bitmap != null){
addBitmapToMemoryCache(key,bitmap);
}
}