要想App漂亮美观,图片是必须使用的,没有那个app说我就显示纯文字吧,图片过多过大就会到账系统OOM,那么如何避免这种情况呢?结合项目经验,本文就总结了一下一些常用的方法,希望大家能留言补充。谢谢!
一、缩小图片
比如在一个图片列表,你需要显示一个200*200像素大小的图片,你不压缩的话,直接将1000*800的原图片显示出来,那么占用内存过多,页面就会卡,甚至OOM导致app奔溃。缩小图片又有两种方法:1、设置BitmapFactory.Options+Bitmap.config缩放比例来达到缩小图片 2、使用matrix+Bitmap.Config缩小图片 ,给出这两种方法的主要代码。
private void initView() {
mImgSample = (ImageView) findViewById(R.id.img_sample);
mTxtmsg = (TextView) findViewById(R.id.txt_msg);
mBtnOptions = (Button) findViewById(R.id.btn_options);
//方法一:通过Options+Bitmap.Config缩放图片
mBtnOptions.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Bitmap bitmapOriginal = BitmapFactory.decodeResource(getResources(), R.mipmap.ab);
int originalSize = getBitmapSize(bitmapOriginal);
Bitmap bitmapNew = decodeSampledBitmapInSizePixels(SampleSizeBitmapActivity.this, R.mipmap.ab, 200, 200, -1);
int newSize = getBitmapSize(bitmapNew);
mTxtmsg.setText("Original image Size(kb):" + originalSize / 1024 + "k\n New image Size(kb):" + newSize / 1024 + "k");
mImgSample.setImageBitmap(bitmapNew);
bitmapOriginal.recycle();//记得不用的及时释放内存
}
});
//方法二:通过Matrix+Bitmap.Config缩放图片
mBtnMatrix = (Button) findViewById(R.id.btn_matrix);
mBtnMatrix.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Bitmap bitmapOriginal = BitmapFactory.decodeResource(getResources(), R.mipmap.ab);
int originalSize = getBitmapSize(bitmapOriginal);
Matrix matrix = new Matrix();
//第一个参数是x轴的缩放比例,而第二个参数是y轴的缩放比例。
matrix.postScale(0.5f, 0.5f);//x,y轴缩放比例必须一致,不然图片会变形,两个0.5,缩放为原来的1/4
Bitmap bitmapNew = Bitmap.createBitmap(bitmapOriginal, 0, 0, bitmapOriginal.getWidth(), bitmapOriginal.getHeight(), matrix, false);
//获得ARGB_565色彩模式一个像素占2个字节的图片,默认是ARGB_8888占4个字节
bitmapNew = bitmapNew.copy(Bitmap.Config.RGB_565, false);
int newSize = getBitmapSize(bitmapNew);
mTxtmsg.setText("Original image Size(kb):" + originalSize / 1024 + "k\n New image Size(kb):" + newSize / 1024 + "k");
mImgSample.setImageBitmap(bitmapNew);
bitmapOriginal.recycle();//记得不用的及时释放内存
}
});
}
//获取图片所占的字节数
public static int getBitmapSize(Bitmap bmp) {
if (bmp == null)
return 0;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) // API 19
return bmp.getAllocationByteCount();
return bmp.getByteCount();
}
/**
* 从项目资源文件加载
* @param context 上下文
* @param resId 项目资源文件编号
* @param maxWidth 缩放图片到最大宽度px为单位
* @param maxHeight 缩放图片到最大的高度px为单位
* @param maxPixels 缩放图片到最大的像素px为单位
* @return
*/
public static Bitmap decodeSampledBitmapInSizePixels(Context context, int resId, int maxWidth, int maxHeight, int maxPixels) {
try {
BitmapFactory.Options opts = new BitmapFactory.Options();
// decode size only,设置这个值表示先不加图片加载到内存
opts.inJustDecodeBounds = true;
BitmapFactory.decodeResource(context.getResources(), resId, opts);
/**
*默认是ARGB_8888,一个像素的a,r,g,b各占一个字节
*ALPHA_8:每个像素占用1byte内存
ARGB_4444:每个像素占用2byte内存
ARGB_8888:每个像素占用4byte内存
RGB_565:每个像素占用2byte内存
*/
opts.inPreferredConfig = Bitmap.Config.RGB_565;
// calc sample size
opts.inSampleSize = calcSampleSizeInSizePixels(opts.outWidth, opts.outHeight, maxWidth, maxHeight, maxPixels);
//内存不足允许回收,避免OOM
opts.inPurgeable=true;
opts.inInputShareable=true;
// decode bitmap
opts.inJustDecodeBounds = false; //设置好上面的值之后,才加载图片到内存
return BitmapFactory.decodeResource(context.getResources(), resId, opts);
} catch (Throwable e) {
Log.w("createThumbnail: ", e);
return null;
}
}
/**
* 从内存卡或sd卡加载
* @param path 文件路径
* @param maxWidth 缩放图片到最大宽度px为单位
* @param maxHeight 缩放图片到最大的高度px为单位
* @param maxPixels 缩放图片到最大的像素px为单位
* @return
*/
public static Bitmap decodeSampledBitmapInSizePixels(String path, int maxWidth, int maxHeight, int maxPixels) {
try {
BitmapFactory.Options opts = new BitmapFactory.Options();
// decode size only
opts.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, opts);
/**
*默认是ARGB_8888
*ALPHA_8:每个像素占用1byte内存
ARGB_4444:每个像素占用2byte内存
ARGB_8888:每个像素占用4byte内存
RGB_565:每个像素占用2byte内存
*/
opts.inPreferredConfig = Bitmap.Config.RGB_565;
// calc sample size
opts.inSampleSize = calcSampleSizeInSizePixels(opts.outWidth, opts.outHeight, maxWidth, maxHeight, maxPixels);
//内存不足允许回收,避免OOM
opts.inPurgeable=true;
opts.inInputShareable=true;
// decode bitmap
opts.inJustDecodeBounds = false;
return BitmapFactory.decodeFile(path, opts);
} catch (Throwable e) {
Log.w("createThumbnail: ", e);
return null;
}
}
//计算缩放比例大于1表示缩小,等于1不缩放,小于1放大
public static int calcSampleSizeInSizePixels(int width, int height, final int maxWidth, final int maxHeight, final int maxPixels) {
final int sampleInWidth = maxWidth > 0 ? width / maxWidth : 1;
final int sampleInHeight = maxHeight > 0 ? height / maxHeight : 1;
final int sampleInPixels = maxPixels > 0 ? (int) Math.ceil(Math.sqrt((width * height) / (double) maxPixels)) : 1;
final int initSampleSize = Math.max(Math.max(Math.max(sampleInWidth, sampleInHeight), sampleInPixels), 1);
// round up to nearest pow of 2
int sampleSize = 1;
while (sampleSize < initSampleSize)
sampleSize = sampleSize << 1;
return sampleSize;
}
二、使用LRUCache
使用缓存避免每次重新load图片到内存中,这是客户端或者是服务端,内存优化常用的方法,通过使用least recently used算法将近期最少使用的那块内存回收,避免OOM,关于LRUCache的使用方法,请看我的另外一篇博客android开发步步为营之88:基于LruCache和AsyncTask的网络相册开发
三、及时进行内存回收
这个其实第一条里面已经用到了,那就是不用的Bitmap,记得及时的回收
if(!bmp.isRecycle() ){
bmp.recycle() // 回收图片所占的内存
system.gc() // 提醒系统及时回收
}
另外参考网上的资料,说使用使用BitmapFactory.decodeStream代替其他decodeResource,decodeFile等方法,因为decodeStream直接调用 JNI>>nativeDecodeAsset()来完成decode,无需再使用java层的createBitmap,也不使用java空间进行分辨率适配,没试验过,也许会更节省内存吧。
先写到这里,后面学到更好的方法,再来补充,明天周末,大家周末愉快!