在编写程序的时候,经常需要显示很多图片,当图片质量较高,尺寸和分辨率较大时,我们的程序可能吃不消!因为程序都有一定的内存大小限制,这就可能会造成OOM(内存溢出)。
那么,该如何解决这个问题呢?思路就是,在展示高分辨率的图片的时候,肯定会对其进行压缩,然后根据控件的大小调整。
最基本的压缩方法:BitmapFactory
BitmapFactory提供了一个Options的方法,该方法里面包含了解析图片的相关参数,当我们第一次解析图片的时候因为不确定图片的大小,所以建议设置Options.InjustdecodeBounds=true,这样的话BitmapFactory.decodeResources(res,id,options)只会去解析图片的宽高和MiME类型,并不会为其分配内存,其中options.outwidth和option.height可以的到图片的宽高,知道宽高以后我们就可以对其进行缩放了,options.simpleSize这个属性可以按比例压缩图片,当然这个simpleSize需要经过计算,最后设置injustdecodebounds=false,就可以返回bitmap了
/**
* @param res getResources
* @param resID R.id.image
* @param w 期望的宽
* @param h 期望的高
*/
public static Bitmap decodeImage(Resources res, int resID, int w, int h) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
//第一次解析图片,不分配内存,只得到宽和高
BitmapFactory.decodeResource(res, resID, options);
options.inSampleSize = calaculateSampleSize(options, w, h);
options.inJustDecodeBounds=false;
//根据得到的宽高再次解析图片
return BitmapFactory.decodeResource(res,resID,options);
}
static int calaculateSampleSize(BitmapFactory.Options options, int w, int h) {
//图片原本的宽和高
int width = options.outWidth;
int height = options.outHeight;
int simpleSize = 1;
if (width > w || height > h) {
int widthRadio = Math.round((float) width / (float) w);
int heightRadio = Math.round((float) height / (float) h);
// 选择宽和高中最小的比率作为inSampleSize的值,这样可以保证最终图片的宽和高
// 一定都会大于等于目标的宽和高。
simpleSize=widthRadio>heightRadio?heightRadio:widthRadio;
}
return simpleSize;
}
OK,这只是加载一张图片,那么问题来了,如果成百上千张图片怎么办呢?程序中我们经常使用listview,girdview,recycleView等联网加载大量图片,在不断滑动的时候会不断的去解析加载图片,结果可想而知,图片不断增加,最终导致OOM,有人会说,我把屏幕之外的图片进行释放,只显示屏幕当中的图片不就行了?听起来好像没错,的确可以减轻程序的内存压力,但是另外一个问题又出现了,我们不断的回收图片,那么重新加载的时候岂不是又消耗了内存、流量和时间?这里不得不提出一个新的概念:缓存
在androidV4包中提供了一个类:LruCache,这个类提供了一套算法,可以把最近使用的对象强引用存储在LinkedHashMap ,把最近最少使用的对象在缓存达到峰值时从内存中删除,那么如何使用呢?
private LruCache<String, Bitmap> mMemoryCache;
@Override
protected void onCreate(Bundle savedInstanceState) {
// 获取到可用内存的最大值,使用内存超出这个值会引起OutOfMemory异常。
// LruCache通过构造函数传入缓存值,以KB为单位。
int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
// 使用最大可用内存值的1/8作为缓存的大小。
int cacheSize = maxMemory / 8;
mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap bitmap) {
// 重写此方法来衡量每张图片的大小,默认返回图片数量。
return bitmap.getByteCount() / 1024;
}
};
}
public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
if (getBitmapFromMemCache(key) == null) {
mMemoryCache.put(key, bitmap);
}
}
public Bitmap getBitmapFromMemCache(String key) {
return mMemoryCache.get(key);
}
public void loadBitmap(int resId, ImageView imageView) {
final String imageKey = String.valueOf(resId);
final Bitmap bitmap = getBitmapFromMemCache(imageKey);
if (bitmap != null) {
imageView.setImageBitmap(bitmap);
} else {
imageView.setImageResource(R.drawable.image_placeholder);
BitmapWorkerTask task = new BitmapWorkerTask(imageView);
task.execute(resId);
}
}
class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
// 在后台加载图片。
@Override
protected Bitmap doInBackground(Integer... params) {
final Bitmap bitmap =decodeImage(getResources(), params[0], 100, 100);
addBitmapToMemoryCache(String.valueOf(params[0]), bitmap);return bitmap;}}
以上内容参考bolg:http://blog.csdn.net/guolin_blog/article/details/9316683