Android-怎样计算Bitmap的内存占用和Bitmap加载优化

介绍

在Android开发中Bitmap肯定是绕不过去的,很多时候我们只是使用图片框架加载图片,具体细节的Bitmap对内存的使用图片框架已经帮我们封装好了。但是对Btimap对内存的影响我们还是需要了解的。

内存占用

首先要清楚Bitmap的文件大小肯定不是实际的内存加载大小。因为文件只是存储的信息,加载到内存中显示出来时还需要经过转换。

获取运行的时的内存占用:
针对Bitmap位图对象,Android的系统框架中的graphics包下的BItmap类。有bitmap.getByteCount()方法获取内存大小,单位字节(byte)
其实本质上Bitmap的内存占用计算非常简单:

基本公式:总内存=宽×高×色彩空间

但是在实际运行中不是这么简单,公式的每个参数都会有被不同因素影响。

影响内存大小的三要素


我们这里不从代码出发,直接说重点不会云山雾罩的。
以前文的公式为基础。
影响内存大小的三要素
1. 图片宽高。
2. 色彩空间
3. 缩放比
下面一个个说明

图片宽高

我们在AS打开图片看到的文件信息
文件信息
其中的1000×447就是图片宽高,或者说是原始宽高。

作用:宽高的乘积描述图片的像素点总数,也就是这个图片由多少像素构成。

它一般不是我们最终加载到内存时的图片宽高,但是可以认为是基础变量。

色彩空间

即Bitmap.Config枚举:

作用:色彩空间描述每个像素点的信息

ARGB_8888:

总共32位(4byte),分别对应4个数值,数值单位为8bit位=1byte字节,分别描述透明度(1个)+RGB通道(3个)。每个字节数值范围0-255。作为Bitmap配置色彩空间的默认值。BitmapFactory加载时默认。
public Bitmap.Config inPreferredConfig = Bitmap.Config.ARGB_8888

RGB_565:

总共16位(2byte),分别对应3个数值,5位(红)+6位(绿)+5位(蓝)分别描述RGB通道。Glide加载时默认使用,DecodeFormat类
public static final DecodeFormat DEFAULT = PREFER_RGB_565

可以看到,RGB_565只需要ARGB_8888的一半大小,代价是没有透明度描述。

缩放比

作用:对图片原始宽高的缩放设置认定为缩放比。

首先需要指出,缩放比在内存的影响上是次方的,影响值=缩放比×缩放比。这是因为缩放比分别作用在宽高上,使用了两次。
缩放比会被很多因素影响。

主动设置:

在解析Bitmap时,有个可选的Options对象,其中inSampleSize参数可以影响缩放比的结果。当使用该参数值时要求大于1且是2的倍数,比如在inSampleSize=2时,缩放比被缩小2倍(该功能只有缩小没有放大的可能),即“缩放比=原始缩放比×(1/2)”。对内存结果的影响是缩小4倍,因为宽/高都被缩小2倍。该值默认不生效,需要手动设置。

被动设置

在系统中主要由,具体运行设备dpi的和图片文件存储的drawable文件dpi层级决定。
首先要引出屏幕密度概念。这是Android为应对众多的不同的屏幕分辨率色设备提出的概念和单位。

  • drawable文件的dpi层级:在Drawable系列文件中保存的Bitmap位图文件。根据Android开发的规范,Drawable的系列文件中的修饰符后命名是有意义的,声明了这个文件所属的Dpi(屏幕密度)层级。文件众多也对应了众多的Android设备屏幕密度。
  • 设备的dpi层级:参考https://material.io/devices/可以了解。

设备和drawable文件的dpi缩放比计算:
比如drawable-xhdpi(320=2*160=2*mdpi)下的bitmap被加载到xxxhdpi(640=4*160=4*mdpi)的Pixel-XL中。
dpi缩放比=设备dpi/drawable的dpi,所以上面的,dpi缩放比=640/320=2。
实际意义就是在高分辨率的xxxhdpi设备中,drawable-xhdpi文件需要放大2倍,即宽高各放大2倍来适应高密度设备。需要指出的是如果Bitmap文件保存在drawable没有后缀的文件中,系统会使用drawable的dpi默认层级就是160;

需要说明的是:这个缩放比只在当Bitmap文件位于drawable这样有dpi层级的文件中时生效。如果Bitmap位图文件位于assets包这样的外部文件或者是URL网络地址,是有没有缩放比的(即默认为1)不影响内存结果。这个也好理解,当读取文件时得不到其他的信息就不需要再处理了。有趣的是Glide也是存在缩放比概念的,但是和dpi无关,和View视图有关。

Glide缩放比=View视图值/原图值 ,这里的值是不固定的,因为缩放比只有一个,但是视图宽高和原图宽高的比值有两个,需要权衡取出能够同时应用于宽高的缩放比。

最终缩放比

最后的缩放比结果:

缩放比=主动设置缩放比*被动设置缩放比

主动缩放比:没有设置时,默认为1,即不影响另外的值。
被动缩放比:这需要看能不能得到设备和drawable的关系(或者其他关系,如Glide的视图图片比例)如果不能得到也是1。

最终公式

基于以上认识,丰富上文的基本公式,可以的得到最终的计算Bitmap内存公式

最终公式:总内存=(原始宽×缩放比)×(原始高×缩放比)×色彩空间

验证

原图:1000宽X447高,位于drawable-xxhdpi(480dpi=3*160dpi)文件包,设备Pixel-XL(560dpi=3.5*160dpi)。主动设置inSampleSize=2。使用默认Bitmap.Config=ARGB_8888

  • 缩放比=主动设置×被动设置(dpi层级关系)=1/2×(560/480)=0.5×1.166=0.5833
  • 色彩空间=ARGB_8888=32bit=4byte
  • 原始大小=1000×447

内存占用=(原始宽×缩放比)×(原始高×缩放比)×色彩空间
=1000×0.5833×447×0.5833×4
=583×260×4
=606320byte
≈0.578MB

最后上图验证:
最终结果

计算结果完美匹配运行结果,这里说明为什么故意省略小数点3位以后,这是对应native层代码的float小数点结果转int时的舍弃。

启示

理解Bitmap的最终内存占用计算原理和内存占用各个参数,我们对Bitmap的处理时就有具体的目标。比如常见的优化Bitmap加载过程,其实就是对Bitmap加载时的各个变量参数设置修改。
常见的Bitmap优化:

  • 修改主动缩放比:目标是修改最终图片加载的宽高,进而优化内存占用。具体就是设置inSampleSize值,如在适当的View上缩放显示适合的bitmap,实现bitmap的高效加载(Glide图片框架就是这样,让显示组件View的宽高的参与缩放比计算)。
  • 修改被动缩放比:理解了被动缩放比的计算,设备dpi层级和图片dpi层级关系。最好的情况就是同样的图片在drawable文件不同的dpi层级包中都有适应具体大小的图片(这样在运行不同设备时,被动缩放比都是1)。否则在设备没有直接对应的dpi的drawable文件时,会影响被动缩放比,而且大多数时候是导致图片放大,且导致内存增大二次方。如刚才的计算过程如果把图片从drawable-xxhdpi(480dpi)移到drawable文件包(默认160)中,同样的加载代码最后内存就会放大9倍。
  • 修改色彩空间:在明确的不需要透明度的情况,使用RGB_565替换ARGB_8888,可以直接达到内存缩小一半的功效。缺点就是有限定条件。(从网络上的博文描述,不推荐使用ARGB_4444替换,因为这样的图片质量太差)
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值