FindJpg(2)-BitMap的高效加载和缓存
2016-06-11 12:56
162 查看
一、BitMap的高效加载
因为这个小项目是从本地加载图片资源,所以会有图片压缩的过程。一般若是加载网络图片,应该在服务端按需压缩图片,这样既能节省流量,又能提高图片加载的流畅度。
BitMap高效加载的核心思想-采用BitmapFactory.Options来加载所需尺寸的图片:
因为许多时候ImageView并没有原始图片那么大,所以没必要将图片的原始尺寸加载进来,按一定的采样率来加载按需的缩小后的图片,会降低内存的占用,从而在一定程度上避免OOM,提高了Bitmap加载时的性能。
通过BitmapFactory.Options设置inSampleSize(即采样率)来缩放图片。例如一张像素为1024x1024的图片,若设置inSampleSize值为2,则采样后的图片为512x512,占用的内存缩小2*inSampleSize即4倍。
获取inSampleSize的主要流程:
将BitmapFactory.Options的inJustDecodeBounds参数设为true并加载图片;从字面上就能看到,inJustDecodeBounds参数设为true时,BitmapFactory只解析图片的原始宽高信息,不会真正地加载图片,所以这个操作使得解码时避免了内存分配。
从BitmapFactory.Options取出图片原始宽高信息,对应于outWidth和outHeight参数。
结合目标View所需大小和采样率的规则(官方文档指出inSampleSize的取值应总是2的指数)。
将BitmapFactory.Options的inJustDecodeBounds参数设为false,然后重新加载图片。
具体代码如下:
publicBitmapdecodeSampledBitmapFromFile(String file, intreqWidth,
intreqHeight){
final BitmapFactory.Options options=newBitmapFactory.Options();
options.inJustDecodeBounds=true;//使BitmapFactory只解析图片原始宽/高信息,不会真正加载图片
BitmapFactory.decodeFile(file,options);
options.inSampleSize=calculateInSampleSize(options,reqWidth,reqHeight);//计算采样率
options.inJustDecodeBounds=false;
return BitmapFactory.decodeFile(file,options);//根据采样率重新加载图片
}
public static int calculateInSampleSize(BitmapFactory.Options options,intreqWidth,intreqHeight){
// 图片的原始宽/高
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;
// 一般建议inSampleSize为2的幂
while((halfHeight/inSampleSize)>=reqHeight&&(halfWidth/inSampleSize)>=reqWidth){
inSampleSize*=2;
}
}
return inSampleSize;
}
}
二、BitMap的缓存
缓存策略主要包含缓存的 添加、获取、删除 这三类操作,因为不管内存缓存还是外存缓存,容量都是有限的,所以要删除旧缓存添加新缓存,如何定义缓存的新旧就对应不同的缓存策略。
目前常用的缓存算法是LRU(Least Recently Used),近期最小使用算法,核心思想是缓存满时,优先淘汰近期最少使用的缓存对象。采用LRU算法的缓存有两种:DiskLruCache和LruCache。
DiskLruCache
用于实现存储设备缓存,通过将缓存对象写入文件系统实现缓存效果。
应用场景一般是为了让用户避免流量消耗,将从网络上加载的图片(或其它文件等)缓存到存储设备上。
这里的FindJpg是从本地加载图片,所以用不到DiskLruCache。
LruCache
内部采用LinkedHashMap以强引用的方式存储外界的缓存对象,它是线程安全的。
LinkedHashMap会根据LRU算法来排列对象的顺序,新加入的对象添加到头部,刚被使用过的对象也被移动到头部,所以在链表尾部的对象是最久没有被使用过的,一旦链表满有新对象加入,就会删除链表尾部的对象。
相关代码如下:
1) 先创建LruCache对象,内存缓存容量设为当前进程可用内存的1/8(安卓官方文档推荐),通过重写sizeOf()方法计算缓存对象(即图片)大小,单位得和总容量一致。
privateImageLoader(Context context){
mContext=context.getApplicationContext();
int maxMemory=(int)(Runtime.getRuntime().maxMemory()/1024);//除1024是为了转KB
int cacheSize=maxMemory/8;
mMemoryCache=newLruCache<String,Bitmap>(cacheSize){
@Override
protected int sizeOf(String key,Bitmap bitmap){//计算Bitmap对象大小
return bitmap.getRowBytes()*bitmap.getHeight()/1024;
}
};
}
2) 编写向LruCache中添加、删除缓存对象(图片)的方法。
private voidaddBitmapToMemoryCache(String
key,Bitmap bitmap){//向LruCache中添加一个缓存对象
if(getBitmapFromMemCache(key)==null){
// Log.e(TAG,"addBitmapToMemoryCache");
mMemoryCache.put(key,bitmap);
}
}
private BitmapgetBitmapFromMemCache(String key){//从LruCache中获取一个缓存对象
return mMemoryCache.get(key);
}//从LruCache中获取一个缓存对象
3) 图片的加载,先尝试从内存加载图片,若内存中没有这张图片,再从外存中加载按需压缩的图片并放入内存缓存起来。
publicBitmaploadBitmap(String uri,intreqWidth,intreqHeight){
Bitmap bitmap=loadBitmapFromMemCache(uri);
if(bitmap!=null){
// Log.e(TAG,"loadBitmapFromMemCache,url:"+uri);
return bitmap;
}
try {
bitmap = loadBitmapFromDisk(uri, reqWidth,reqHeight);
a6d5
if (bitmap != null) {
// Log.e(TAG, "loadBitmapFromDisk,url:" + uri);
return bitmap;
}
} catch (IOException e) {
e.printStackTrace();
}
return bitmap;
}
privateBitmaploadBitmapFromDisk(String url, intreqWidth,
intreqHeight)throwsIOException {
if (Looper.myLooper() == Looper.getMainLooper()) {
Log.w(TAG,"load bitmap from UI Thread, it's not recommended!");
}
Bitmap bitmap = null;
bitmap = mImageResizer.decodeSampledBitmapFromFile(url,reqWidth,reqHeight);
if (bitmap != null) {
addBitmapToMemoryCache(url, bitmap);
}
return bitmap;
}
privateBitmaploadBitmapFromMemCache(String url){
Bitmap bitmap=getBitmapFromMemCache(url);
return bitmap;
}
Demo地址:FindJpg
因为这个小项目是从本地加载图片资源,所以会有图片压缩的过程。一般若是加载网络图片,应该在服务端按需压缩图片,这样既能节省流量,又能提高图片加载的流畅度。
BitMap高效加载的核心思想-采用BitmapFactory.Options来加载所需尺寸的图片:
因为许多时候ImageView并没有原始图片那么大,所以没必要将图片的原始尺寸加载进来,按一定的采样率来加载按需的缩小后的图片,会降低内存的占用,从而在一定程度上避免OOM,提高了Bitmap加载时的性能。
通过BitmapFactory.Options设置inSampleSize(即采样率)来缩放图片。例如一张像素为1024x1024的图片,若设置inSampleSize值为2,则采样后的图片为512x512,占用的内存缩小2*inSampleSize即4倍。
获取inSampleSize的主要流程:
将BitmapFactory.Options的inJustDecodeBounds参数设为true并加载图片;从字面上就能看到,inJustDecodeBounds参数设为true时,BitmapFactory只解析图片的原始宽高信息,不会真正地加载图片,所以这个操作使得解码时避免了内存分配。
从BitmapFactory.Options取出图片原始宽高信息,对应于outWidth和outHeight参数。
结合目标View所需大小和采样率的规则(官方文档指出inSampleSize的取值应总是2的指数)。
将BitmapFactory.Options的inJustDecodeBounds参数设为false,然后重新加载图片。
具体代码如下:
publicBitmapdecodeSampledBitmapFromFile(String file, intreqWidth,
intreqHeight){
final BitmapFactory.Options options=newBitmapFactory.Options();
options.inJustDecodeBounds=true;//使BitmapFactory只解析图片原始宽/高信息,不会真正加载图片
BitmapFactory.decodeFile(file,options);
options.inSampleSize=calculateInSampleSize(options,reqWidth,reqHeight);//计算采样率
options.inJustDecodeBounds=false;
return BitmapFactory.decodeFile(file,options);//根据采样率重新加载图片
}
public static int calculateInSampleSize(BitmapFactory.Options options,intreqWidth,intreqHeight){
// 图片的原始宽/高
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;
// 一般建议inSampleSize为2的幂
while((halfHeight/inSampleSize)>=reqHeight&&(halfWidth/inSampleSize)>=reqWidth){
inSampleSize*=2;
}
}
return inSampleSize;
}
}
二、BitMap的缓存
缓存策略主要包含缓存的 添加、获取、删除 这三类操作,因为不管内存缓存还是外存缓存,容量都是有限的,所以要删除旧缓存添加新缓存,如何定义缓存的新旧就对应不同的缓存策略。
目前常用的缓存算法是LRU(Least Recently Used),近期最小使用算法,核心思想是缓存满时,优先淘汰近期最少使用的缓存对象。采用LRU算法的缓存有两种:DiskLruCache和LruCache。
DiskLruCache
用于实现存储设备缓存,通过将缓存对象写入文件系统实现缓存效果。
应用场景一般是为了让用户避免流量消耗,将从网络上加载的图片(或其它文件等)缓存到存储设备上。
这里的FindJpg是从本地加载图片,所以用不到DiskLruCache。
LruCache
内部采用LinkedHashMap以强引用的方式存储外界的缓存对象,它是线程安全的。
LinkedHashMap会根据LRU算法来排列对象的顺序,新加入的对象添加到头部,刚被使用过的对象也被移动到头部,所以在链表尾部的对象是最久没有被使用过的,一旦链表满有新对象加入,就会删除链表尾部的对象。
相关代码如下:
1) 先创建LruCache对象,内存缓存容量设为当前进程可用内存的1/8(安卓官方文档推荐),通过重写sizeOf()方法计算缓存对象(即图片)大小,单位得和总容量一致。
privateImageLoader(Context context){
mContext=context.getApplicationContext();
int maxMemory=(int)(Runtime.getRuntime().maxMemory()/1024);//除1024是为了转KB
int cacheSize=maxMemory/8;
mMemoryCache=newLruCache<String,Bitmap>(cacheSize){
@Override
protected int sizeOf(String key,Bitmap bitmap){//计算Bitmap对象大小
return bitmap.getRowBytes()*bitmap.getHeight()/1024;
}
};
}
2) 编写向LruCache中添加、删除缓存对象(图片)的方法。
private voidaddBitmapToMemoryCache(String
key,Bitmap bitmap){//向LruCache中添加一个缓存对象
if(getBitmapFromMemCache(key)==null){
// Log.e(TAG,"addBitmapToMemoryCache");
mMemoryCache.put(key,bitmap);
}
}
private BitmapgetBitmapFromMemCache(String key){//从LruCache中获取一个缓存对象
return mMemoryCache.get(key);
}//从LruCache中获取一个缓存对象
3) 图片的加载,先尝试从内存加载图片,若内存中没有这张图片,再从外存中加载按需压缩的图片并放入内存缓存起来。
publicBitmaploadBitmap(String uri,intreqWidth,intreqHeight){
Bitmap bitmap=loadBitmapFromMemCache(uri);
if(bitmap!=null){
// Log.e(TAG,"loadBitmapFromMemCache,url:"+uri);
return bitmap;
}
try {
bitmap = loadBitmapFromDisk(uri, reqWidth,reqHeight);
a6d5
if (bitmap != null) {
// Log.e(TAG, "loadBitmapFromDisk,url:" + uri);
return bitmap;
}
} catch (IOException e) {
e.printStackTrace();
}
return bitmap;
}
privateBitmaploadBitmapFromDisk(String url, intreqWidth,
intreqHeight)throwsIOException {
if (Looper.myLooper() == Looper.getMainLooper()) {
Log.w(TAG,"load bitmap from UI Thread, it's not recommended!");
}
Bitmap bitmap = null;
bitmap = mImageResizer.decodeSampledBitmapFromFile(url,reqWidth,reqHeight);
if (bitmap != null) {
addBitmapToMemoryCache(url, bitmap);
}
return bitmap;
}
privateBitmaploadBitmapFromMemCache(String url){
Bitmap bitmap=getBitmapFromMemCache(url);
return bitmap;
}
Demo地址:FindJpg
相关文章推荐
- android日常开发总结的技术经验60条
- 聊聊Linux动态链接中的PLT和GOT(1)——何谓PLT与GOT
- ubuntu12.04卸载wine
- 第十六周项目二 小玩文件(1)
- R Programming Assignment 1
- 一、设计模式概述
- 【CSS笔记六】CSS盒模型
- 关于炉石的奥弹打死精灵龙的分析
- 【OS】磁盘调度算法
- 计算农历24节气
- 开机自启动nginx
- 体验Remix——安卓电脑
- spring配置文件中autowire详解
- 我的个人博客搭建记录
- 如何显示二进制流的图片(利用img控件)
- 【R】提升R代码运算效率的11个实用方法
- JavaScript手机振动API
- Java 类的方法总结-目前网上最完整9种方法总结
- 定义自己的rm command
- jquery.cookie用法