Android图片加载优化

Android图片加载优化

在Android开发中图片加载往往是导致OOM(Out of Memory)的主要原因,所以图片的压缩不得不作为Android开发中比用的一项技能点,以下将以简单的方式进行优化。

图片的大小如何被定义?

其实图片大小的计算是很简单的,只需要用图片的width乘以图片的height然后再乘以每一个像素所占用的字节数,这个字节数需要根据图片解码模式来获得,Android中提供了6种方案,不过常用的只有三个,在下方已经列出。

A:透明度(Alpha)

R:红色(Red)

G:绿(Green)

B:蓝(Blue)

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字节)

基础知识补脑:
8bit(位)=1byte(字节)
1024byte=1KB
1024kb=1MB
1024mb=1GB

举例:
1、480x800的图片,在色彩模式为ARGB_8888的情况下

1
480*800*4/1024/1024=1.46484375MB

2、480x800的图片,在色彩模式为ARGB_4444的情况下

1
480*800*2/1024/1024=0.732421875MB

通过以上的测试,发现导致过大的原因主要集中在两点上,一点是图片的宽和高零一点则是解码方式,通过等比例缩放图片以及切换解码方式可以有效的降低图片的内存占用。不过在以下的优化方案中还有一种优化就是内存复用技术。

优化方案

缩放图片+质量压缩

1
2
3
4
5
6
7
8
var bitmapOptions = BitmapFactory.Options()
bitmapOptions.inJustDecodeBounds = true//设置仅加载图片的宽高信息不将图片加载的内存中
BitmapFactory.decodeResource(resources, R.drawable.testimg, bitmapOptions)//获取图片的宽高
bitmapOptions.inSampleSize = 4//设置缩放比例(1、缩放图片)
bitmapOptions.inPreferredConfig = Bitmap.Config.RGB_565//设置解码模式(2、质量压缩)
bitmapOptions.inJustDecodeBounds = false//消除仅加载图片的宽高
var bitmap = BitmapFactory.decodeResource(resources, R.drawable.testimg, bitmapOptions)//加载图片到内存(注意添加Option对象)
this.imgMain.setImageBitmap(bitmap)//使用Bitmap

inSampleSize的作用就是可以把图片的宽高缩小到inSampleSize分之一,例如inSampleSize是4那么最终的宽高就是原来的宽高除4。单纯的缩放四分之一是不够的,只能说是治标不治本,好比一个高清50MB的图片,你缩放4倍还是凉凉,所以说还是根据尺寸来计算需要缩放的倍数。

PS:这里的inSampleSize要求是2的平方,如果不是则会被自动转换最为接近的数值。

1
2
//设置缩放比例(1、缩放图片)
bitmapOptions.inSampleSize =Math.ceil(bitmapOptions.outWidth.toDouble() / targetWidth.toDouble()).toInt()

PS:以上的缩放并非精确的缩放

抽取方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//Java描述

/**
* 加载图片
* @param path
* @param targetWidth
* @return
*/
private Bitmap loadBitmap(String path, int targetWidth, Bitmap.Config config) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, options);
int inSampleSize = options.outWidth / targetWidth;
options.inSampleSize = inSampleSize;
options.inJustDecodeBounds = false;
options.inPreferredConfig = config;
Bitmap bitmap = BitmapFactory.decodeFile(path, options);
return bitmap;
}

内存复用

何为内存复用,通俗来说就是我已经创建了一个Bitmap对象了,那么我接着还想用一个bitmap对象,那么就可以复用上一个Bitmap对象,不过这样做有两个缺点,第一就是会将之前的图片覆盖掉,第二就是后边加载的图片必须小于或者等于之前的图片大小,否则就不行。

1
2
3
4
var bitmapOptions1 = BitmapFactory.Options()
bitmapOptions1.inBitmap = bitmap//绑定一个已经加载的Bitmap对象
var bitmap1 = BitmapFactory.decodeResource(resources, R.drawable.timg, bitmapOptions1)//加载新的Bitmap对象
this.imgMain1.setImageBitmap(bitmap1)//使用Bitmap

其他方案

推荐使用WEBP格式代替PNG和JPEG图片格式。

写在最后

以上所说的只是简单的图片压缩优化,并非特定专业的优化方式,不过应对一般的项目需求应该还是可以满足的。