Bitmap内存分析

注意: 本文大部分数据是在SDK25, cpu架构为armeabi v7a, Android Studio 3.4上测试得出, 不同的系统版本和硬件可能有差异.

Bitmap内存计算方法

Bitmap在Android中是一个普通Java类, 关于Java类在内存中所占的大小可以参考Shallow Size和Retained Size详解.

在不同的Android版本中, Bitmap的定义有细微差别, 不过大致由两部分组成, 辅助变量Byte数组.

  1. Byte数组就是指保存所有像素的颜色信息的数组, 是Bitmap的核心信息, 一般也是内存大头.

  2. 辅助变量就是指Bitmap类内定义的除了Byte数组外的其他变量, 包括了图像的宽高等信息, 不同的SDK版本可能有所不同.

apk运行时, 所有Bitmap实例的辅助变量的内存的Shallow Size是相同的.

在SDK25中实测, 辅助变量的Shallow Size43Byte

本文主要关注Byte数组, 它的计算公式为

Byte数组大小 = 图像宽 * 图像高 * 单个像素所占字节

图像宽高可以直接通过Bitmap属性读取到. 所以

计算Bitmap内存的关键在于, 它用多少个字节表示单个像素

注意

图片文件的宽高不一定等于Bitmap宽高, 因为加载非屏幕对应dpidrawable资源文件时, 加载时会对图像进行缩放.

像素配置: ARGB_8888, RGB_565, ALPHA_8

我们通过BitmapFactory创建Bitmap时, 可以传BitmapFactory.Options控制加载配置, 可以通过inPreferredConfig参数来配置一个Bitmap.Config来指定加载配置.

一般常用的配置有ARGB_8888, RGB_565, ALPHA_8, 用来描述如何保存像素

  1. ARGB_8888表示一个像素有4个通道, 每个通道8bit, 共32bit, 所以单个像素占4byte
  2. RGB_565表示一个像素有3个通道, 没有透明通道, 其中R通道5bit, G通道6bit, B通道5bit, 共16bit, 所以单个像素占2byte
  3. ALPHA_8表示像素只有透明通道, 占1byte, 一般代码创建Bitmap用到, 用来创建透明遮罩用来进行图像计算.

但是注意, 该配置只是一个倾向, 如果需要加载的图像不能以该配置加载, 那么就会由系统自行选择合适的配置来加载.

例如, 带透明像素的图片不能以RBG_565的方式加载, 即使配置成RGB_565, 最终还是会以ARGB_8888来加载, 单个像素还是占4byte.

图片格式和像素配置的关系

以下结论都是在SDK25中实验得出

  1. PNG-32格式只能使用ARGB_8888配置加载, 设置RGB_565ALPHA_8无效
  2. PNG-24和PNG-8格式可以使用ARGB_8888RGB_565两种配置加载
  3. JPG/JPEG格式可以使用ARGB_8888RGB_565两种配置加载, 设置ALPHA_8无效
  4. WEBP格式, 当图中不包含透明像素时, 可以使用ARGB_8888RGB_565配置加载, 当图中包含透明像素时, 则只能使用ARGB_8888配置加载, 设置ALPHA_8总是无效的

在Android Studio打开格式为PNG, JPG和WEBP的图片时, 可以看到右上角有提示(如下图)该图标的像素深度, 常见的有32bit, 24bit和8bit, 可以根据这个值判断图片能否使用RGB_565加载. 像素深度为32bit的图片只能通过ARGB_8888加载

Android Studio图像预览, 像素深度提示

RGB_565的效果

以下结论都是在SDK25中实验得出

因为使用RGB_565时, 只能使用5bit表示红色通道和蓝色通道, 用6bit表示绿色通道, 所以颜色会和实际值有差别

经试验得出: 加载进内存时会移除低位, 显示时会在低位用1或0补全8位

我并没有看补全位数的源码, 根据现象推测是根据颜色值的大小来补位, 例如

图像实际颜色是R=240, G=8, B=30, #FF0081E, 通过RGB_565加载后, 内存中实际的颜色值是R=247, G=8,B=24, #F70818, 其中红色通道进行了1补位, 绿色通道进行了0补位, 蓝色通道进行了0补位, 拿蓝色通过举例

// 蓝色通道原始颜色
30=0x1E=0001 1110
// 通过RGB_565加载时需要移除3位低位
0001 1110 -> 0001 1xxx
// 显示时使用0补位
0001 1xxx -> 0001 1000=0x18=24

黑科技: 索引位图(PNG8)

PNG格式

参考PNG-8、24、32区别介绍

PNG实际上是存在多种格式的, 而在Android中, 普通场景下说的PNG是指PNG32, 即单个像素是由4通道, 每通道8bit表示的, 所以每个像素占32位.

另外在官方文档资源类型-位图介绍中有提到, aapt工具在打包时可以自动转成PNG-8.

:在构建过程中,可通过 aapt 工具自动优化位图文件,对图像进行无损压缩。例如,不需要超过 256 色的真彩色 PNG 可通过调色板转换为 8 位 PNG。这样产生的图像质量相同,但所需内存更少。因此请注意,此目录中的图像二进制文件在构建时可能会发生变化。如果您打算以比特流的形式读取图像,进而将其转换为位图,请改为将图像放在 res/raw/ 文件夹中,避免系统对其进行优化。

索引位图内存

本节内容参考Android 内存优化之索引颜色位图(上)

具体的原理看上述链接即可, 核心原理是

通过设置BitmapFactory.Option#inPreferredConfignull, 来触发底层Skia加载索引位图的逻辑, 索引位图的特点是有一个色盘区域储存图像中使用的颜色, 而图像中每个像素用1byte来表示色盘中的颜色下标, 因此内存仅占ARGB_8888的1/4, 可以大大降低Bitmap的内存占用.

注意

虽然索引位图可以降低内存, 但是对图片本身的限制较多, 因为每个像素只占1byte, 也就是只能表示0-255, 所以索引位图最多只能包含256种颜色, 所以只适用于色彩较少的图像, 尤其不适用于有渐变颜色的图像, 另外通过实测, 还有需要注意

PNG-8的图可以直接放在drawable目录中, 但是通过普通资源id设置或者xml显示时, 还是会加载成ARGB_8888, 需要手动通过BitmapFactory加载才能优化内存.

Android Studio profiler内存工具观察色值

可以通过Android Studio的profiler抓内存看Bitmap实例对应的byte数组, 规律如下

  1. 如果是ARGB_8888, 4个一组, 顺序为R-G-B-A

  2. 如果是RGB_565, 2个一组, 低位在前, 高位在后, (可能和CPU架构有关), 例如

    从profiler中观察到  98, 8
    实际应为
    8 98 = 0000 1000 0110 0010 -> R=00010, G=000011, B=00010
    
  3. profiler中的十进制负数需要转成二进制补码才是正确的颜色值, 例如

    ARGB_8888中观察到R=-16
    -16的机器码 -> 1001 0000
    1001 0000反码 -> 1110 1111
    1001 0000补码 = 反码 + 1 -> 1111 0000
    所以实际色值
    R=1111 0000=0xF0=240
    
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 221,576评论 6 515
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,515评论 3 399
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 168,017评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,626评论 1 296
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,625评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,255评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,825评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,729评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,271评论 1 320
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,363评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,498评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,183评论 5 350
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,867评论 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,338评论 0 24
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,458评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,906评论 3 376
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,507评论 2 359

推荐阅读更多精彩内容