决定Bitmap图片大小或者解码之后内存取决于:图片透明度,位深,图片采样率(分辨率),编码格式,图片本身宽高,下面我们围绕这几个因素来研究下
1:质量压缩法(改变图片位深和透明度)
1.1 :理论说明
所谓质量压缩法:不减少图片本身的像素,它在保持像素的前提下该变图片的位深以及透明度,来达到压缩图片的目的,压缩后的文件大小会有所改变,但是导入成 bitmap后所占内存是不会变化的,由于要保持像素不变,所以它无法无限压缩,达到一个极限值后就不会继续变小了。
所以这个方法,不适合缩略图,也不适合想通过压缩图片来减少内存的占用。它仅仅适用在想保证图片质量的前提下,同时减少文件大小的情况而已。
1.2:Demo 代码
ByteArrayOutputStream baos = new ByteArrayOutputStream();
image.compress(Bitmap.CompressFormat.JPEG, 100, baos);
int options = 100;
while ( baos.toByteArray().length / 1024>32) {
baos.reset();
image.compress(Bitmap.CompressFormat.JPEG, options, baos);
options -= 10;
}
ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());
Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null);
2:采样率压缩法(改变图片分辨率)
2.1 理论说明
限制图片的采样率可以直接限制解码读取到图片的大小,即图片分辨率大小,但是采样率是整数,这就会造成我们不能根据图片实际宽高来精确的给出图片采样率。
所以我们一般都过下面的方式来计算图片分辨率
1:首先获取一张图片的大小和内存
2:根据获取到图片的宽高,结合我们需要的宽高,来重新计算图片的采样率
3:得到采样率之后,重新解码图片,就可以根据需要的大小来获取对应分辨率的图片
2.2 Demo代码说明
1:首先获取一张图片的大小
BitmapFactory.Options options = new BitmapFactory.Options();
// itmapFactory.Opitions的inJustDecodeBounds属性为true,就可以在不分配内存的情况下,获取该图片的大小和类型。否则一旦图过大,在解码这个图片时,可能会出线内存泄漏
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
String imageType = options.outMimeType;
2: 获取到高度和宽度之后,就可以按照我们的需要的高度和宽度,来计算图片的缩放比,得出图片的采样率
public static int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
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;
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) > reqHeight
&& (halfWidth / inSampleSize) > reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
3:得到采样率之后,重新解码图片,就可以根据需要的大小来获取对应分辨率的图片
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
3:缩放法
3.1 理论说明
缩放法其实就是,通过设置好的 matrix , 然后再 createBitmap ,相当于将图片按照一定比例缩放。至于按照多大的比例,你可以按照经验值 进行0.8倍缩放,也可以循环缩放直到合适的尺寸
3.2 代码说明
ByteArrayOutputStream out = new ByteArrayOutputStream();
image.compress(Bitmap.CompressFormat.JPEG, 85, out);
float zoom = (float)Math.sqrt(size * 1024 / (float)out.toByteArray().length);
Matrix matrix = new Matrix();
matrix.setScale(zoom, zoom);
Bitmap result = Bitmap.createBitmap(image, 0, 0, image.getWidth(), image.getHeight(), matrix, true);
out.reset();
result.compress(Bitmap.CompressFormat.JPEG, 85, out);
while(out.toByteArray().length > size * 1024){
System.out.println(out.toByteArray().length);
matrix.setScale(0.9f, 0.9f);
result = Bitmap.createBitmap(result, 0, 0, result.getWidth(), result.getHeight(), matrix, true);
out.reset();
result.compress(Bitmap.CompressFormat.JPEG, 85, out);
}
4:RGB_565法
4.1 理论分析
图片默认的编码格式 是 ARGB_8888 ,转换成 ARGB_565可以减少一半内存消耗,可以解决一些OOM问题,但是 RGB_565是没有透明度的,如果图片本身需要保留透明度,就只能使用ARGB_8888。
ARGB -----分别代表图片:Alpha 透明度和 red green blue三原色
4.2 :常见的图片编码格式
在 Bitmap.Config中,图片列举了下面几种编码格式
ARGB_8888:分别用8位来记录4个值,所以每个像素会占用32位。
ARGB_4444:分别用4位来记录4个值,所以每个像素会占用16位。
RGB_565:分别用5位、6位和5位来记录RGB三色值,所以每个像素会占用16位。
ALPHA_8:根据注释应该是不保存颜色值,只保存透明度(8位),每个像素会占用8位。
如果我们以ARGB_8888作为基准来进行对比
- ARGB_4444:内存占用减少一半,但是每个值图片失真度很严重,这个参数本身已经不推荐使用了。
- RGB_565:内存占用减少一半,舍弃了透明度,同时三色值也有部分损失,但是图片失真度很小。
- ALPHA_8:内存占用减少3/4,没有颜色,只有透明度,即黑白。
4.3:总结
由于ARGB_4444已废除,而ALPHA_8需要在特殊条件下使用,一般用来做特殊需求的,所以我们大多数是用的还是ARGB_8888和RGB_565。
RGB_565能够在保证图片质量的情况下大大减少内存的开销,是解决oom的一种方法。但是一定要注意RGB_565是没有透明度的,如果图片本身需要保留透明度,那么就不能使用RGB_565。