一、OOM异常是什么?产生OOM异常的原因 OOM(Out Of Memory--内存不够用了)
a、计算图片占用的内存大小:
1、 占用内存 = 图片的长度 * 图片的宽度 * 单位像素占用的字节数;
2、 单位像素占用的字节数是由BitmapFactory.Options的inPreferredConfig变量的值决定的:
A(alpha)--透明度,R(red)--红色,G(green)--绿色,B(blue)--蓝色,即三原色;如一张图片是720*1080,ARGB是一
种色彩模式:
① Bitmap.Config.ALPHA_8: 此时图片就只有alpha值,一个像素就只占用一个字节(8代表的是这个透明度占用8
位,即一个字节),图片占用的内存大小:720*1080*1(字节)
② Bitmap.Config.ARGB_4444: 即A、R、G、B 各占四个bit,总共就是4*4=16个bit= 2(字节),这种格式的图片看
起来质量很差,已经不再使用, 图片占用的内存大小:720*1080*2(字节)
③ Bitmap.Config.ALPHA_8888: 即A、R、G、B各占8个bit,总共就是4*8=32bit = 4(字节),这是一种高质量的图
片,android手机上Bitmap默认的格式,图片占用的内存:720*1080*4(字节)
④ Bitmap.Config.RGB_565 : 即 R、G、B 分别占用5、6、5个bit,总共:5+6+5=16bit=2(字节),这种格式的图片,
不支持透明和半透明,图片质量也达到了效果,且占用内存相对来说较小,图片占用内存:720*1080*2(字节)
b、 一个应用程序,android系统一般会为它分配16MB的内存大小,,例如:三星s4后置摄像头像素1300万,则拍照后,照片
的大小就是1300万*4(字节)(假设单位像素的字节数按4个字节),即49.6MB,远远超过了android系统分配给这个应
用的内存,且这16MB不仅仅是用来存储图片的,所以如果不处理图片,直接加载图片的话,就会出现OOM异常。这是
android开发中最常见的OOM异常。
二、解决OOM异常的方法:
a、改变图片的大小,即缩小图片,缩小图片的方法:
方法一:利用Matrix来缩小图片
public Bitmap zoomBitmap(Bitmap bitmap) {
//获得Bitmap的高和宽
int bmpWidth = bitmap.getWidth();
int bmpHeight = bitmap.getHeight();
//这边假设imageView的宽和高是一样的
int imageViewHeight = imageViewWidth;
//设置缩小比例,imageViewWidth 就是我们需要的宽度
double scale = (double) imageViewWidth / bmpWidth;
double scaleH = (double) imageViewHeight/bmpHeight;
//为了保证图片可以显示完全
if (scale>scaleH){
scale = scaleH;
}
Log.e("缩放比例:", "scale = " + scale);
//计算出长宽要缩放的比例
float scaleWidth = (float) (1 * scale);
float scaleHeight = (float) (1 * scale);
//产生resize后的Bitmap对象
Matrix matrix = new Matrix();
matrix.postScale(scaleWidth, scaleHeight);
Bitmap resizeBmp = null;
resizeBmp = Bitmap.createBitmap(bitmap, 0, 0, bmpWidth, bmpHeight, matrix, true);
Log.e("缩放后的图片的宽度:bitmapWidth=", "" + resizeBmp.getWidth());
//修改色彩模式
Bitmap zoomBitmap = resizeBmp.copy
(Bitmap.Config.RGB_565,false);
return zoomBitmap;
}
优点:可以按任何比例来缩放,即缩放比例可以是小数;而Options.inSampleSize 只能是整数。
缺点:会耗费很大的内存,因为缩放后的图片的色彩模式是ARGB_8888,每个像素单位就是4个字节了;
解决办法:修改色彩模式,如代码所示,这样虽然会生成一个Bitmap对象,但是总的消耗的内存还是减小的。
方法二:利用BitmapFactory.Options进行缩放:
private Bitmap getimage(String srcPath) {
BitmapFactory.Options newOpts = new BitmapFactory.Options();
//开始读入图片,此时把options.inJustDecodeBounds 设回true了
newOpts.inJustDecodeBounds = true;
BitmapFactory.decodeFile(srcPath, newOpts);//此时返回bitmap为空,只有一些图片大小等信息;
//这时候设置false ,返回的bitmap就不是空的了
newOpts.inJustDecodeBounds = false;
int w = newOpts.outWidth;
int h = newOpts.outHeight;
//imageViewWidth和imageViewHeight是我们希望的宽和高
int hh = imageViewWidth;
int ww = imageViewWidth;
//缩放比。由于是固定比例缩放,只用高或者宽其中一个数据进行计算即可
int be = 1;//be=1表示不缩放
if (w > h && w > ww) {//如果宽度大的话根据宽度固定大小缩放
be = (int) (newOpts.outWidth / ww);
} else if (w < h && h > hh) {//如果高度高的话根据宽度固定大小缩放
be = (int) (newOpts.outHeight / hh);
}
//be不能是小数,必须是整型的
if (be <= 0)
be = 1;
newOpts.inSampleSize = be;//设置缩放比例
//重新读入图片,注意此时已经把options.inJustDecodeBounds 设回false了
bitmap = BitmapFactory.decodeFile(srcPath, newOpts);
//直接返回,不用质量压缩也可以展示
return bitmap;
}
优点:可以通过options.inJustDecodeBounds=true,来节省内存的开销
缺点:options.inSampleSize只能是2的整数次幂,如果不是的话,就向下取最大的2的整数次幂
b、改变图片的质量,但是这样会造成图片看起来不清晰:
private Bitmap compressQualityImage(Bitmap image) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
//100代表不压缩图片,把图片保存到baos中
image.compress(Bitmap.CompressFormat.JPEG, 100, baos);
int options = 100;
//500KB 就是我们能用的最大内存空间了
while (baos.toByteArray().length / 1024 > 500) {
//清空baos
baos.reset();
options -= 10;
if (options < 0) {
break;
}
image.compress(Bitmap.CompressFormat.JPEG, options, baos);
}
//把压缩后的图片放在inBm中
ByteArrayInputStream inBm = new ByteArrayInputStream(baos.toByteArray());
//生成图片
Bitmap bitmap = BitmapFactory.decodeStream(inBm, null, null);
return bitmap;
}
c、目前我知道的最优方案,实际应用中也高高的
分析:当我们使用BitmapFactory.decodeResource()、decodeFile()等方法时,这些方法最终都是通过java层的
createBitmap()方法来生成Bitmap的,这样就会消耗很多的内存;因此我们如果用BitmapFactory.decodeStream()来生成一个Bitmap对象时,无需调用java层的
createBitmap()方法,而是直接调用JNI的nativeDecodeAsset()方法来完成,节省很多的内存空间,如果再加上图片的Config(色彩模式)参数(最好用
Bitmap.Config.RGB_565)这样就更加有效地减少的内存的开销。
代码:
/**
*相对来说最优生成Bitmap的方法
* @param context
* @param resId
图片的id,一般在drawable下的图片,代码中也有如何根据图片路径来构造流
* @return
*/
public static Bitmap readBitmap(Context context, int resId) {
BitmapFactory.Options opt = new BitmapFactory.Options();
//最低位的配置
opt.inPreferredConfig = Bitmap.Config.RGB_565;
//inPurgeable设为true的话表示使用BitmapFactory创建的Bitmap用于存储Pixel的内存空间在系统内存不足时可以被回收
opt.inPurgeable = true;
//是否深拷贝???
opt.inInputShareable = true;
//1、drawable下的图片构造流的方法
InputStream is =
context.getResources().openRawResource(resId);
//2、根据路径构造流的方法
try {
String picturePath = "..........";
File file = new File(picturePath);
InputStream inputStream = new FileInputStream(file);
}catch (Exception e){
e.printStackTrace();
}
return BitmapFactory.decodeStream(is, null, opt);
}
d、注意:只用一次的Bitmap的记着回收,代码如下:
if(!bitmap.isRecycled()){
bitmap.recycle();//回收图片所占的内存
bitmap = null;
System.gc();//提醒系统及时回收
}
参考资料:http://www.cnblogs.com/plokmju/p/android_loadbigimage.html
最后
以上就是飘逸刺猬最近收集整理的关于android 加载图片oom异常的全部内容,更多相关android内容请搜索靠谱客的其他文章。
发表评论 取消回复