本篇文章主要是聊聊压缩的优化,尽量不贴代码,文章结构也尽量保证短小。
比例压缩
比例压缩是一种非常常见的算法,主要是通过设置
BitmapFactory.Options.inSampleSize
来压缩,我们可以自己设置压缩的比例,也可以根据目标控件的宽、高度来等比设置比例,压缩效率一般,所以我们时常会配合质量压缩一块来压缩。
质量压缩
拿到等比压缩过后的图片,我们可以通过 bitmap.compress(Bitmap.CompressFormat.JPEG, quality, baos)
来实现质量压缩,我们主要关注以下俩个参数:
1、CompressFormat
是我们要压缩的格式,包含的格式有:
- JPEG:压缩为 JPEG 格式,压缩质量随
quality
改变,一般都用它 - PNG:压缩为 PNG 格式,PNG 是无损的,他会忽略
quality
值,一般不用 - WEBP:压缩为 WEBP 格式,压缩质量随
quality
改变,但从 Android Q 开始,quality=100
时,将会采用无损压缩,一般不用
2、quality
是我们要压缩的质量,压缩区间为 0 ~ 100,一般我们会设置压缩质量为 80.
色值压缩
色值压缩通过 BitmapFactory.Options
的 inPreferredConfig
来设置,对应的 config 有:
- ALPHA_8
- RGB_565
- ARGB_4444
- ARGB_8888
- RGBA_F16
- HARDWARE
这几种配置都是通过设置单个像素点的位数来改变图片大小,比如 ARGB_8888 和 RGB_565,前者采用 4*8 = 32 位来表示一个像素,后者采用 5+6+5 = 16 位来表示一个像素,在质量大小上面,RGB_565 比 ARGB_8888 小了 1 倍。
算法压缩
前面讲了这么多,都逃脱不了压缩的界定值问题,就是到底设置什么样的值,既能保证图片清晰,还能保证大小压缩已经到了极致。这个地方又要引入一个第三方框架,那就是 Luban,Luban 的精华部分就是实现了一套 inSampleSize 的算法计算,保证图片的压缩是极致的,具体代码可以查看 computeSize 方法,这里就不贴代码了。
Native 压缩
虽然 Luban 将压缩发挥了极致,但毕竟只是计算 inSampleSize,相比较 native 的 libjpeg 而言,压缩又进了一个等级。关于 libjpeg 的 分析可以看我这篇文章《JNI 之 Libjpeg 分析》,Luban 的作者也做了一个基于 libjpeg 的库—— Luban-Turbo。
问题
上面的所有算法,都有一个致命问题 —— 压缩前需要加载进内存。对于大图而言,100M 的图片加载进内存会导致内存拼命增长,发生 OOM 会导致应用进程崩溃。
方案
压缩前需要加载进内存这个貌似无法解决,但我们可以另辟新径,开个新的进程出来,专门来处理图片压缩,即使加载进的图片非常大导致进程崩溃,也不会影响到主进程。
对于开个新进程来处理图片压缩问题,我想到的一个比较简单的方案是创建一个专门处理压缩的 Activity,并设置 process 为单独的一个进程:
<activity android:name=".ProcessActivity" android:process=":compress"/> 复制代码
如果体验想更佳的话,我们可以给 activity 设置 dialog theme,然后布局设置为一个 loading 加载框等待压缩,压缩完成后,setResult 将压缩结果带回: