一、图片Bitmap.Config选项

ARGB8888:ARGB四个通道,每个通道8bi,是一个像素4个字节;

RGB565:每个像素占2Byte,其中红色占5bit,绿色占6bit,蓝色占5bit,是一个像素2个字节,如果不需要 alpha 通道,特别是资源本身为 jpg 格式的情况下,用这个格式比较理想;

ALPHA_8:只有一个alpha通道,没必要用,因为我们随便用个颜色就可以搞定的。适合类似二维码一类的简单黑白图;

ARGB_4444:这个从API 13开始不建议使用,因为质量太差。

二、图片格式对比

JPEG:是一种广泛使用的有损压缩图像标准格式,它不支持透明和多帧动画,一般摄影的作品是JEPG格式的,通过控制压缩比,可以调整图片的大小。

PNG:是一种无损压缩的图片格式,他支持完整的透明通道,从图片处理的领域来讲,JEPG只有RGB三个通道,而PNG有ARGB四个通道,因此PNG图片占用空间一般比较大,会无形的增加app的大小,在做app瘦身时一般都要对PNG图片进行梳理以减小其占用的体积。

GIF:是一种古老的图片的格式,诞生于1987年,随着初代互联网流行开来,他的特别是支持多帧动画,表情图。

Webp:google于2010年发布,支持有损和无损、支持完整的透明通道、也支持多帧动画,目前主流的APP都已经使用了Webp,淘宝,微信,即保证了图片的大小和质量。这种格式Android 4.0后开始支持


同样一张图片,jpg比png 会多少小一些(甚至很多),原因jpg是一种有损压缩的图片存储格式,而png则是无损压缩的图片存储格式,所以jpg会比png小,当然代价也是显而易见的。

JPG 不适用于所含颜色很少、具有大块颜色相近的区域或亮度差异十分明显的较简单的图片。对于需要高保真的较复杂的图像,PNG 虽然能无损压缩,但图片文件较大。

如果仅仅是为了 Bitmap 读到内存中的大小而考虑的话,jpg 也好 png 也好,没有什么实质的差别;

二者的差别主要体现在:

> alpha 你是否真的需要?如果需要 alpha 通道,那么没有别的选择,用 png。

> 你的图色值丰富还是单调?就像刚才提到的,如果色值丰富,那么用jpg,如果作为按钮的背景,请用 png。

> 对安装包大小的要求是否非常严格?如果你的 app 资源很少,安装包大小问题不是很凸显,看情况选择jpg或者png。

> 目标用户的 cpu 是否强劲?jpg 的图像压缩算法比 png 耗时。

三、减小图片图片所占内存

1、inSampleSize:通过采样的方式缩小图片尺寸,用来模糊图片比较好。内存小了,图片尺寸也小了。

2、Matrix:大图小用用采样,小图大用用矩阵。

Matrix matrix = new Matrix();
matrix.preScale(2, 2, 0, 0);
canvas.drawBitmap(bitmap, matrix, paint);

需要注意的是,在使用搭载 5.1.1 原生系统的 Nexus6 进行测试时发现,如果使用 Canvas 的 setMatrix 方法,可能会导致与矩阵相关的元素的绘制存在问题。因此请尽量使用 canvas 的 scale、rotate 这样的方法,或者使用 concat 方法。

把图片放到 ImageView 当中一样可以:

Matrix matrix = new Matrix();
matrix.postScale(2, 2, 0, 0);
imageView.setImageMatrix(matrix);
imageView.setScaleType(ScaleType.MATRIX);
imageView.setImageBitmap(bitmap);

3、options.inJustDecodeBounds = true;//如果inJustDecoedBounds设置为true的话,解码bitmap时可以只返回其高、宽和Mime类型,而不必为其申请内存,从而节省了内存空间。

4、尽量自定义一个 View,覆写 onDraw 自己画图。

四、索引位图Skia

在开发基于Android平台的专业图像处理软件,由于Android平台对应用程序的内存限制以及java的处理效率问题,需要用到NDK+Skia的方式来进行,采用Skia直接绘制屏幕要比用Java对象绘制屏幕少消耗至少一半的内存,其实在Android内部,仔细查看源代码的话,其实Android平台本身的图像引擎就是基于Skia 2D图像引擎的,但由于Skia 本身不是很稳定,所以Skia并没有被包含在NDK的发布包中。

索引位图:每个像素只占 1 Byte,不仅支持 RGB,还支持 alpha,但是Android 官方并不支持这个。不过,Skia 引擎是支持的。

canUpscalePaletteToConfig 函数如果返回false,那么 colorType 就被置为 kIndex_8_SkColorType 了。如果传入的 dstColorType 是 kRGB_565_SkColorType,同时图片还有 alpha 通道,那么返回 false。

dstColorType 就是我们在 decode 的时候,传入的 Options 的inPreferredConfig。

try {
   Options options = new Options();
   options.inPreferredConfig = Config.RGB_565;
Bitmap bitmap = BitmapFactory.decodeStream(getResources().getAssets().open("index.png"), null, options);
   imageView.setImageBitmap(bitmap);
} catch (IOException e) {
    e.printStackTrace();
}

得到的图片占用内存只有ARGB 8888 的1/4。不过由于官方并未做出支持,因此这个方法有诸多限制,比如不能在 xml 中直接配置,,生成的 Bitmap 不能用于构建 Canvas 等等。

五、内存复用

inBitmap属性来对Bitmap做内存复用,从而避免内存的重新分配。当然inBitmap是有较大限制的,有着一定的场景依赖,所以通常被使用的频率不是很高。

简单的适合使用inBitmap属性的场景:拍照!一个ImageView用来多次展示不同图片。

byte[] welcome1 = Tool.readFile(this, bitmapPaths[index++]);
if (bitmap == null) {
	BitmapFactory.Options option1 = new BitmapFactory.Options();
	option1.inMutable = true;
    bitmap = BitmapFactory.decodeByteArray(welcome1, 0, welcome1.length, option1);
} else {
    BitmapFactory.Options option1 = new BitmapFactory.Options();
    option1.inBitmap = bitmap;
    option1.inMutable = true;
    bitmap = BitmapFactory.decodeByteArray(welcome1, 0, welcome1.length, option1);
}
imageView.setImageBitmap(bitmap);
if (index >= 3) {
    index = 0;
}

六、大图加载

BitmapRegionDecoder主要用于显示图片的某一块矩形区域,如果你需要显示某个图片的指定区域,那么这个类非常合适。

推荐几种图片处理网站:

无损压缩ImageOptin,在不牺牲图片质量的前提下,即减下来PNG图片占用的空间,又提高了图片的加载速度 https://imageoptim.com/api

有损压缩ImageAlpha,图片大小得到极大的缩小,如果需要使用的话,一定要ui设计师看能否使用 https://pngmini.com/