参考文章: 最详细的Android图片压缩攻略,让你一次过足瘾
bitmap内存计算方式:
bitmap内存大小 = (图片长度 x 图片宽度)(分辨率压缩) x 单位像素占用的字节数 (改变编码格式);
- 不过,这样的说法并不准确,我们常说的 图片长* 图片宽,这里最终需要换成px作为单位去计算图片大小。在 Android 原生的 Bitmap 操作中,某些场景下,图片被加载进内存时的分辨率会经过一层转换,所以,虽然最终图片大小的计算公式仍旧是分辨率像素点大小,但此时的分辨率已不是图片本身的分辨率了;
-
而且当图片放在 res 内的不同目录中,最终图片加载进内存所占据的大小会不一样,因为系统在加载 res 目录下的资源图片时,会根据图片存放的不同目录做一次分辨率的转换,而转换的规则是
-
新图的高度 = 原图高度 * (设备的 dpi / 目录对应的 dpi )
新图的宽度 = 原图宽度 * (设备的 dpi / 目录对应的 dpi ) -
因此同一个 app,但跑在不同 dpi 设备上,同样的界面,但所耗的内存有可能是不一样的
两种bitmap内存优化方式:
1.质量压缩(不会减少图片本身大小,但是会减少file文件大小)
在Android中,对图片进行质量压缩,通常我们的实现方式如下所示:
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
//quality 为0~100,0表示最小体积,100表示最高质量,对应体积也是最大
bitmap.compress(Bitmap.CompressFormat.JPEG, quality , outputStream);
在上述代码中,我们选择的压缩格式是CompressFormat.JPEG,除此之外还有两个选择:
其一,CompressFormat.PNG,PNG格式是无损的,它无法再进行质量压缩,quality这个参数就没有作用了,会被忽略,所以最后图片保存成的文件大小不会有变化;
其二,CompressFormat.WEBP,这个格式是google推出的图片格式,它会比JPEG更加省空间,经过实测大概可以优化30%左右。
Bitmap的compress这个API 最后调用了函数encoder->encodeStream(…)编码保存本地。该函数是调用skia引擎来对图片进行编码压缩;Skia 是一个 Google 自己维护的 c++ 实现的图像引擎,实现了各种图像处理功能,并且广泛地应用于谷歌自己和其它公司的产品中(如:Chrome、Firefox、 Android等)
Skia 在 Android 中提供了基本的画图和简单的编解码功能,实际会调用 libjpeg.so 动态库进行编码压缩,Android编码保存图片的逻辑是Java层函数→Native函数→Skia函数→对应第三库函数(例如libjpeg)。 libjpeg是一个C语音编写的高效JPEG图像处理库,Android系统在7.0版本之前内部使用的是libjpeg非turbo版,并且为了性能关闭了Huffman编码(optimize_coding 设为 false),使用的是默认的哈夫曼表。在7.0之后的系统内部启用了Huffman进行编码,根据实际图片去计算相对应的哈夫曼表。
去获取每一个元素,对于图片就是每一个像素中 argb 的权重,去循环整个图片的像素信息,这无疑是非常消耗性能的,所以早期 android 就使用了默认的哈夫曼表进行图片压缩。
哈夫曼算法(还有定长算术编码)举例: Android 中图片压缩分析(上) - 腾讯云开发者社区-腾讯云
所以想要提升图片压缩率的可以从libjpeg库着手,网上资料也不少,后续有机会可以测试一下其他可替代的so库。
2.尺寸压缩(包含采样率压缩 + 长宽缩放)
inJustDecodeBounds为true会禁止为bitmap分配内存,获取到bitmap的长宽值,计算出insampelsize采样率;
为什么要比较实际宽高和目标宽高比呢?
假设有张原图宽高3840x2400,要压缩成1920x1080,实际不可能100%压缩到这个值,因为要图片压缩保持宽高比,所以采用优先保证以最短边进行比例缩放;
计算较短边举例: 3840/1920 = 2, 2400/1080 = 2.2,因此看出高缩放程度更大,优先保证高的缩放,scale = 2400/1080=2.22,因此宽width = 2840/2.2 = 1728,最终结果1728x1080;
鲁班库和微信是会设置长宽边不同的缩放阶层,根据不同的长宽比例,设置不同压缩率;
而Driver app项目中会设定一个最短边标准960, 如果长和宽中的最短值大于960,将会一直压缩,直到这个最短边小于等于960;
鲁班框架的缺点:
-
不支持多文件合理并行压缩
-
质量压缩写死 60不能自己选
-
没有提供图片输出格式选择
-
可能出现内存泄漏,需要自己合理处理生命周期
总流程如下:
3.改变编码格式:
Bitmap裁剪方法(createBitmap):
public static Bitmap createBitmap (Bitmap source, int x, int y, int width, int height, Matrix m, boolean filter)
matrix:来实现旋转等多种效果的截图;
Bitmap source:需要截图的原始图
int x:起始x坐标
int y:起始y坐标
int width:要截的图的宽度
int height:要截的图的宽度