Android Bitmap
Android 中的图片是以 Bitmap 方式存在的,绘制的时候也是 Bitmap,直接影响到 app 运行时的内存。
在 Android 中 Bitmap 所占用内存的计算公式是:
图片长度 * 图片宽度 * 像素点的字节数
Bitmap.getByteCount()、Bitmap.getAllocationByteCount()和Bitmap.compress(...)
public int getByteCount()
返回可用于存储该位图像素的最小字节。
从 Build.VERSION_CODES.KITKAT开始,该方法结果不再用于确定 bitmap 所使用的内存。可以使用 getAllocationByteCount()
public int getAllocationByteCount()
返回用于存储该位图像素已经分配的内存大小。
如果重用位图以解码其他更小尺寸的位图或者手动重新配置,该方法的结果可能大于 getByteCount() 的结果。例如 reconfigure(int, int, Config), setWidth(int), setHeight(int), setConfig(Bitmap.Config), 和 BitmapFactory.Options.inBitmap。如果没有以这种方式修改 bitmap,那么它将和 getByteCount() 的值相同。
public boolean compress(Bitmap.CompressFormat format, int quality, OutputStream stream)
将压缩的位图放到指定的输出流,如果返回 true,通过将相应的输入流传递给 BitmapFactory.decodeStream() 可以重建位图。注意,并非所有的 Formats 都支持位图的直接配置,所以从 BitmapFactory 返回的位图可能是不同的位深。或者丢失每个像素的 Alpha。例如:jpeg 仅支持不透明像素
这个方法可能需要几秒才能完成,所以应该再工作线程调用它。
图片格式
类型 | 描述 | eg |
---|---|---|
ALPHA_8 | 每个像素都存储一个半透明通道 | 0000 0000 8bits 1B |
ARGB_4444 | 此字段已在 API 13中弃用。此配置质量较差,建议使用ARGB_8888 | 0000 0000 0000 0000 16bits 2B |
ARGB_8888 | 每个像素存储4个字节 | 0000 0000 0000 0000 0000 0000 0000 0000 32bits 4B |
RGB_565 | 每个像素存储在2个字节中,只有 RGB 通道被编码:红色以5位精度存储(32个可能值),绿色以6位精度存储(64个可能值),蓝色存储为5位精度 | 0000 0000 0000 0000 16bits 2B |
压缩方法
- 质量压缩
private Bitmap compressQuality(Bitmap bitmap){
Bitmap bm = BitmapFactory.decodeResource(getResource(), R.drawable.tes)
int beforeBmCount = bitmap.getByteCount();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 80, baos);
int compressedLen = baos.toByteArray().length;
Bitmap compressBm = BitmapFactory.decodeByteArray(out.toByteArray(), 0, compressedLen);
int afterBmCount = compressBm.getByteCount();
return compressBm;
}
结果:
1. beforeBmCount
和 afterBmCount
是相等的。
2. 输出流的长度 compressedLen
是变小了
经过 BitmapFactory.decodeByteArray() 对压缩后的 byte[] 进行解码后得到的 bitmap 和未压缩的是一样的。
Bitmap.compress()压缩的是存储大小,即保存在本地 disk 的大小(通过流的方式,流确实变小了)。
质量压缩不会减少图片的像素,它是在保持像素的前提下改变图片的位神及透明度,来达到压缩图片的目的,图片的长、宽、像素都不会改变,那么 bitmap 所占内存大小是不会变的。
第二个参数 quality,可以调节你压缩的比例,但值得注意的是,质量压缩对 png 这种无损压缩是没有作用的。
- 采样率压缩
private Bitmap compressSampling(){
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 2;
Bitmap bm = BitmapFactory.decodeResource(getResources(), R.drawable.test. options);
return bm;
}
采样率压缩的原理是缩放 bitmap 的尺寸,通过调节 inSampleSize 参数,比如调节为2,宽高就会为原来的1/2,内存变回原来的 1/4
- 放缩法压缩
private Bitmap compressMatrix(Bitmap bm){
Matrix matrix = new Matrix();
matrix.setScale(0.5f, 0.5f);
Bitmap.createBitmap(bitmap, 0, 0, bm.getWidth, bm.getHeigh, matrix, true);
}
放缩法通过矩阵对图片进行裁剪,也是通过缩放图片尺寸,来达到压缩图片的效果,和采样率原理一样。
- RGB_565压缩
private Bitmap compressRGB565(){
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.RGB_565;
return BitmapFactory.decodeResource(getResource(), R.drawable.test, options);
}
这里通过压缩像素占用的内存来达到压缩的效果,一般不建议使用 ARGB_4444,画质很低,如果对透明度没有要求,建议改成 RGB_565,相比 ARGB_8888将节省一般的内存开销。
- createScaledBitmap
private Bitmap compressScaleBitmap(Bitmap bm){
Bitmap bitmap = Bitmap.createScaleBitmap(bm, 600, 900, true);
return bitmap;
}
将图片的大小压缩成用户期望的大小,来减少内存。
case
description
创建桌面快捷方式时失败,甚至部分机型崩溃。
analyse
1. 版本适配问题,26以上方式不一样
2. Bitmap 太大,发生异常
项目中已经进行了适配,对 bitmap 进行了分析,最终锁定 bitmap 太大,导致问题。
solve
使用createScaledBitmap的方式更为简单,下面使用压缩方式为例编写:
ByteArrayOutputStream baos = new ByteArrayOutStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);
byte[] bytes = baos.toByteArray();
BitmapFactory.Options options = new BitmapFactory.Options();
// 只获取图片的大小信息,而不是将整张图片载入在内存中,避免内存溢出
options.inJustDecodeBounds = true;
BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options);
int height = options.outHeight;
int width = options.outWidth;
int inSampleSize = 2;
int minLen = Math.min(height, width);
if(minLen > 100) {
float ratio = (float) minLen / 100.0f;
inSampleSize = (int) ratio;
}
// 计算好压缩比例,设置加载原图
options.inJustDecodeBounds = false;
// 设置计算好的压缩比例
options.inSampleSize = inSampleSize;
Bitmap bm = BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options);
...
// 内存优化
// 1. 内存泄漏
// 2. 内存占用 bitmap 压缩
bitmap.recycle()
参考学习:
https://developer.android.com/reference/android/graphics/Bitmap.html
https://blog.csdn.net/adam_ling/article/details/52346741
https://www.jb51.net/article/132775.htm
https://blog.csdn.net/u010652002/article/details/72676723