[关闭]
@946898963 2022-11-14T10:38:49.000000Z 字数 3497 阅读 1421

Bitmap.Config

Bitmap


Android是一个内存相当吃紧的系统,那么在做程序的过程中使用内存就需要相当谨慎,而我们接触最大的大对象估计就是Bitmap了,下面就根据Bitmap.Config值的介绍来看下Bitmap在内存中存储的形式,在实际场景中选择合适的配置进行Bitmap存储。

Bitmap.Config代表了Bitmap可以的配置情况,配置描述的是这些像素信息是如何存储的,这会影响到了图片质量和透明度。

Bitmap.Config的属性,它是由一个enum(枚举)类型构成的,其中拥有6中枚举可以选择。我们选择其中最常用的4个来着重说明一下。下面是Bitmap.Config的源码:

  1. public enum Config {
  2. ALPHA_8 (1),
  3. RGB_565 (3),
  4. @Deprecated
  5. ARGB_4444 (4),
  6. ARGB_8888 (5),
  7. RGBA_F16 (6),
  8. HARDWARE (7);
  9. final int nativeInt;
  10. private static Config sConfigs[] = {
  11. null, ALPHA_8, null, RGB_565, ARGB_4444, ARGB_8888, RGBA_F16, HARDWARE
  12. };
  13. Config(int ni) {
  14. this.nativeInt = ni;
  15. }
  16. static Config nativeToConfig(int ni) {
  17. return sConfigs[ni];
  18. }
  19. }
  1. public enum Config {
  2. ALPHA_8 (1),
  3. RGB_565 (3),
  4. RGBA_F16 (6),
  5. ARGB_8888 (5),
  6. }

以上4种是我们最常用的属性设置,ARGB_4444头上有个被遗弃的注解,这个值在level 13的时候就已经不被建议使用了,因为他是一个低质量的配置,它被建议使用ARGB_8888,在Android 4.4及其以上的系统中,设置的ARGB_4444会被系统使用ARGB_8888替换。

首先,我们来看一下第一个,也就是ALPHA_8,这种枚举类型是可以充当swith语句中的case条件的,所以枚举用起来也特别方便。优点是在于它是线程安全的单利模式,也支持序列化。缺点就是大量使用会导致性能降低

  • ALPHA_8

每个像素储存一个透明度值,每个像素是占1个字节(8位),也就是后面8的由来。其次,它不储存任何颜色信息,只单纯做透明度的处理,这对于设置遮罩的图片用例十分有用。根据变量名称,我们可以初步的知道它要表达的信息,以下也是如此。

每个像素使用一个独立的alpha通道存储,该通道占用8bit,即:每个像素占用1byte = 8bit / 8内存。注意:此属性会导致RGB通道信息丢失,只剩下透明度,所以基本不会用到。

  • RGB_565

RGB_565,如变量名所述。R代表Red,G代表Green,B代表Blue。那么565就是储存它们的方式,它的每个像素占用2个字节。红色占5位,绿色占6位,蓝色占5位(5位保存的值2的5次方也就是32种的可能性,比如00001,00002...。同理6位则有64种值)。虽然它能提供这么多种颜色值,但是唯独不可以透明。因为它没有提供保存透明度的通道,所以通常它用于一些不透明的图片的设置。

只编码RGB通道信息,没有透明alpha通道信息,Red红色通道信息占用5位内存,Green绿色通道信息占用6位内存,Blue蓝色通道信息占用5位内存。每个像素占用2 byte= (5bit + 6bit + 5bit) / 8 内存,支持2^16 = 65535种颜色。质量较好。

所以直接设置 RGB_565:

  • 对于一张透明图片(png),内存、宽高不变,bitmap 也不会失去透明度。
  • 对于一张非透明图片(png、jpg),宽高不变,内存减小。
    copy 一遍可以减少内存,但生成的 bitmap 会失去透明度,透明处变黑。

copy 一遍可以减少内存,但生成的 bitmap 会失去透明度,透明处变黑。

  1. val bmScaled = bmRaw.copy(Bitmap.Config.RGB_565, true)

如果不需要alpha通道,特别是资源本身为jpg格式的情况下,用RGB_565格式解码更加节省内存。

  • ARGB_8888

也如其名,它提供了颜色和透明度的通道,综合了以上两种方式。它每个像素占用4个字节(显然图片的大小也增加),并且它每个都用8位来保存其颜色和透明度值。我们知道8位将有256种可能值(支持2^32 = 1600w种颜色),这意味着它的颜色值的可能性越多,图片的质量、显示效果将大大提高。所以,这也是被推荐的一种属性设置,这也是Bitmap默认的颜色配置信息。

  • RGBA_F16

这个与上面变量名又有点区别,从F16就可以看出。它每个像素占用8个字节,但是它是以半浮点数存储的,那前面的F就说的通了。Google的注释里还指明这个属性非常适合用于广色域宽屏和HDR(高动态范围的图片)。所以以此来看,它所占用的内存是最高的,因此显示的效果也非常好。

RGB_565ARGB_8888节省内存相信很多同学都知道,但是为什么RGB_565更节省内存?通过上面的介绍我们可以知道,这是因为RGB_565牺牲了alpha通道,不支持透明度,并且RGB每个通道信息较ARGB_888更少。

通常我们优化Bitmap时,当需要做性能优化或者防止OOM(Out Of Memory),我们通常会使用Bitmap.Config.RGB_565这个配置,因为Bitmap.Config.ALPHA_8只有透明度,显示一般图片没有意义,Bitmap.Config.ARGB_4444显示图片不清楚,Bitmap.Config.ARGB_8888占用内存最多。

Glide加载图片默认格式RGB565,Picasso为ARGB8888,默认情况下,Glide占用内存会比Picasso低,色彩不如Picasso鲜艳,自然清晰度就低。

bitmap内存占用计算

Android中bitmap的内存占用是跟图片的尺寸(高和宽)相关。一张图片的内存占用大致的计算公式如下:

  1. 占用内存 = 图像像素总和(width x height)再 x 每像素(bitmap config)占用的字节数

以下是通过代码准确计算:

  1. public static int getSizeInBytes(@Nullable Bitmap bitmap) {
  2. if (bitmap == null) {
  3. return 0;
  4. }
  5. if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) {//API 19
  6. try {
  7. return bitmap.getAllocationByteCount();
  8. } catch (NullPointerException npe) {
  9. // Swallow exception and try fallbacks.
  10. }
  11. }
  12. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1) {//API 12
  13. return bitmap.getByteCount();
  14. }
  15. return bitmap.getHeight() * bitmap.getRowBytes(); //earlier
  16. }

比如拿ARGB_8888来说,它所拥有的颜色种类就是红色:2^8,绿色:2^8,蓝色:2^8,也就是大约1600多万种颜色。这么多种颜色组成的一张图片,比如1920*1080像素的一张图,以ARGB_8888的方式来存储颜色值的话。那它的大小就是1920*1080*4(字节) =8294400(bytes)= 7.91兆(M)大约值。像现在的手机摄像头动不动就是上千万像素,拍出来的照片如果按默认的ARGB_8888 config加载,则至少是几十M的内存占用。

当然,如果用RGBA_F16的方式储存颜色值,那将在16M左右的一张图,这对手机来说已经是一张非常高清的图片了。

参考链接:

Bitmap.Config 属性详解及不同格式图片区别介绍

Bitmap.Config 详解

Android bitmap config你理解对了吗? (有实际使用过程中遇到的问题)

[Android]修改Bitmap的Config格式设置及其Config参数源码阅读(有实际使用过程中遇到的问题)

Android笔记:Bitmap.Config与内存占用的关系

Android Bitmap最全面详解

android Bitmap getByteCount和getRowBytes

Android 计算Bitmap大小 getRowBytes和getByteCount()

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注