在Android开发中,图片一直是应用中占据内存最大的一部分,大图加载甚至会直接造成应用的崩溃,而我们日常所需要进行的性能优化图片压缩更是必不可少的一部分,所以合理的应用图片压缩就显得尤为重要。
1. 认识图片内存的计算
如果要压缩一张图片,我们首先要对一张图片大小的构成有一个简单的理解,这里有一张美女的图片
我们在属性中查看一下它的信息,这个美女的分辨率是1080*1920.那么它在我们的Android图片中加载成bitmap是多大呢,我们打个log
Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.c);
Log.d("TAG",bitmap.getAllocationByteCount()+"===");
结果是8294400。
它的由来是8294400= 1080×1920×4。
2. 为什么要乘以4?(了解图片的基本属性)
Android中图片有四种属性,分别是:
ALPHA_8:每个像素占用1byte内存
ARGB_4444:每个像素占用2byte内存
ARGB_8888:每个像素占用4byte内存 (默认)
RGB_565:每个像素占用2byte内存
A:透明度(Alpha)
R:红色(Red)
G:绿(Green)
B:蓝(Blue)
Android默认以ARGB_8888的方式加载图片,所以一个像素点要占据4个字节的内存。
3. 这四个字节都让谁给占了?
Bitmap.Config ARGB_8888:由4个8位组成,即A=8,R=8,G=8,B=8,那么一个像素点占8+8+8+8=32位(4字节)
Bitmap.Config ARGB_4444:由4个4位组成,即A=4,R=4,G=4,B=4,那么一个像素点占4+4+4+4=16位 (2字节)
Bitmap.Config RGB_565:没有透明度,R=5,G=6,B=5,,那么一个像素点占5+6+5=16位(2字节)
Bitmap.Config ALPHA_8:每个像素占8位,只有透明度,没有颜色
这样明白了吧。
小提示
你如果要真实测试的时候这个资源图片应该放在对应的drawable分辨率下,Android计算资源图片加载内存的时候如果你已经把图片放在了指定的分辨率文件中,它就会直接依据该分辨率加载该图片。
虽然我们可以根据Android中图片的属性来压缩图片,但是今天我们要讲的是根据采样率来压缩图片,也就是我们平时所说的质量压缩。
4.(正题)质量压缩
质量压缩也就是采样率压缩,它压缩的本质就是本来一张图片的宽有1080个像素,我们采样率为2就是每两个像素取一个来采样,不采样的就会被舍弃,所以说如果采样率过高图片就会模糊不清。
4.1 主体方法
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);
//通过指定分辨率来确定采样率的大小
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
options.inJustDecodeBounds = false; //这个时候就要分配内存了
return BitmapFactory.decodeResource(res, resId, options);
}
calculateInSampleSize(options, reqWidth, reqHeight)中参数的宽高应当是根据我们实际开发需要来确定。
4.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;
}
例如,分辨率为 2048x1536 且以 4 作为 inSampleSize 进行解码的图片会生成大约 512x384 的位图(说人话就是一张横向有2048个像素,纵向有1536个像素的图片每4个像素取一个所生成的图片就是我们压缩生成的512x384的图片)。将此图片加载到内存中需使用 0.75MB,而不是完整图片所需的 12MB(假设位图配置为 ARGB_8888)。
采用下面的方法,您可以轻松地将任意大尺寸的位图加载到显示 100x100 像素缩略图的 ImageView 中,如以下示例代码所示:
imageView.setImageBitmap(
decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));
参考文档:https://developer.android.com/topic/performance/graphics/load-bitmap