1 . 为了处理安卓中Bitmap图片尺寸过大,导致内存OOM,我们一般会对Bitmap处理一下,一般处理分为两步。
- 第一步,获取原始Bitmap的尺寸信息,然后我们设置一些相关配置,来尽量压缩图片
//这个函数就是相关配置Bitmap信息
BitmapFactory.Options options = new BitmapFactory.Options();
//这个表示,我们在创建bitmap时,只获取相关参数,不生成bitmap对象
options.inJustDecodeBounds = true;
//设置图片色彩模式,也有RGB_565,ARGB_8888表示每个像素占4个字节,RGB_565表示每个像素占2个字节
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
//对图片宽高进行压缩,如果值为2,则表示图片宽高分别为1/2,像素则减少了原始的1/4
//一般来说这个samplesize可以有我们自己计算
options.inSampleSize = 2;
下面是一个完整的代码,只计算配置信息,不生成bitmap
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
options.inPreferredConfig = Bitmap.Config.RGB_565;
//执行这个方法,我们获取到了option相关参数
BitmapFactory.decodeResource(getResources(), R.drawable.unnamed, options);
//计算samplesize ,根据你想要设置的宽高和原始图片比较,
//当然你也可以随便设置,2,3,4都行
int sampleSize = calculateSampleSize(options, 400, 400);
private int calculateSampleSize(BitmapFactory.Options options, int reqW, int reqH) {
int sampleSize = 1;
int real_h = options.outHeight;
int real_w = options.outWidth;
if (real_h > reqH || real_w > reqW) {
int sam_h = Math.round((float) real_w / (float) real_h);
int sam_w = Math.round((float) real_w / (float) reqW);
sampleSize = Math.min(sam_h, sam_w);
}
return sampleSize;
}
- 第二部,将上面计算优化过的Option参数,传入到Bitmap的创建函数,获取优化后的Bitmap对象
options.inJustDecodeBounds=false;
options.inSampleSize = sampleSize;
options.inPreferredConfig = Bitmap.Config.RGB_565;
Bitmap real = BitmapFactory.decodeResource(getResources(), R.drawable.unnamed, options);
2 . bitmpa在内存中计算,分两种情况
- 情况一BitmapFactory.decodeResource()
我们这里放一张原始像素720*405的图片分别到到mipmap xhdpi文件夹中和mipmap xxhdpi
BitmapFactory.Options options = new BitmapFactory.Options();
Bitmap real = BitmapFactory.decodeResource(getResources(), R.mipmap.unnamed, options);
1.inDensity 取决于文件夹也就是xxhdpi还是xhdpi像素密度。取决于TypedValue,可以看下BitmapFactory.decodeResource方法源码。
2.inTargetDensity 取决于手机屏幕密度。
3.底层源码中源码会对bitmap进行缩放,缩放系数就是=inTargetDensity/inDensity,
- 像素字节数取决于BitmapFactory.Options.Config.ARGB***的设置,ARGB_8888表示每个像素占4个字节,RGB_565占2个字节
- 所以最终bitmap在内存中大小为 size = (原始宽)x(原始高)x(inTargetDensity/inDensity)x(inTargetDensity/inDensity)x像素字节数
Log.d(TAG, "ffff: density = " + options.inDensity + " target = " + options.inTargetDensity + "" +
" size = " + real.getByteCount() + " w= " + real.getWidth() + " h = " + real.getHeight());
/xhdpi: density = 320 target = 420 size = 2010960 w= 945 h = 532
/xxhdpi: density = 480 target = 420 size = 892080 w= 630 h = 354
- 情况二 BitmapFactory.decodeStream()
我们从assets文件夹照片加载图片,也可以从手机外部存储加载,以文件流的形式导入
1.分析点进去看BitmapFactory.decodeStream源码可知,并由没对Options.inTargetDensity和Options.inDensity进行赋值,默认都为0
2.像素字节数取决于BitmapFactory.Options.Config.ARGB***的设置,ARGB_8888表示每个像素占4个字节,RGB_565占2个字节
3.所以此时bitmap在内存中占据的大小=(原始宽)x(原始高)x像素字节数
BitmapFactory.Options options = new BitmapFactory.Options();
try {
InputStream bitmap=getAssets().open("unnamed.jpg");
Bitmap real=BitmapFactory.decodeStream(bitmap);
Log.d(TAG, "ffff: density = " + options.inDensity + " target = " + options.inTargetDensity + "" +
" size = " + real.getByteCount() + " w= " + real.getWidth() + " h = " + real.getHeight());
//打印信息: density = 0 target = 0 size = 1166400 w= 720 h = 405
} catch (IOException e) {
e.printStackTrace();
}
3 . bitmpa.getByteCount vs bitmpa.getAllocationByteCount区别
- 这两个方法都是计算bitmap占据内存大小
- getAllocationByteCount这个方法Added in API level 19 ,默认情况这两个返回结果相同,所以我们SD大于19时,计算Bitmap大小最好用这个方法
假设内存已经存在bitmap1 ,大小1234,此时我有新建了一个bitmap2 其实就是对bitmap1缩小了一倍。
这时因为安卓已经支持对Bitmap的复用,完全没必要重新去在建立一个bitmap对象,浪费性能重新计算和编码bitmap
我们直接复用bitmap1,在进行处理复用处理之后,此时Bitmap2对象的大小getByteCount 可能等于500,但是getAllocationByteCount一定等于1234
参考
细说Bitmap