美文网首页性能优化—Android安卓面试
Android性能调优(5)—Bitmap内存模型

Android性能调优(5)—Bitmap内存模型

作者: godliness | 来源:发表于2018-05-07 09:42 被阅读19次

前面关于内存优化文章中我们也有提到过:Bitmap可以说是内存中的一个大胖子。它对内存的影响极大。

例如:一张1920 * 1080像素 如果按照ARGB_8888来显示的话(1920 * 1080 * 4byte)占用近8M的内存空间。仅仅一张图片就占用了我们应用8M内存资源。

其实作为现在从事Android开发,对于大多数情况,我们一般都会使用第三方库来获取、解码和显示应用程序中的位图,这些第三方库抽象出了大部分复杂问题。使得我们只需要简单的API调用即可完成位图的显示。

但是我们还是有必要去了解下Android中有关Bitmap的处理机制。以便日后在应对Bitmap问题上无从下手。

本篇主要与大家一起讨论下:Bitmap内存模型在Android版本迭代中发生的变化、以及内存占用大小与在Android中特殊情景

一、Bitmap内存模型

随着Android版本的演进,对于Bitmap在Android内存模型,系统也有着不同的情况。

1、Android3.0之前

在Android3.0之前,Bitmap的内存模型是在Native层,此时不受JVM垃圾收集器的管理,主要是强调通过Bitmap.recycle()方式显示的调用以便回收Bitmap占用的内存空间。

注意:只有当确定当前Bitmap不再被引用的时候才可以调用此方法,否则会抛出:“Canvas trying to use a recycled bitmap”错误。

2、Android3.0之后

在Android3.0之后,Bitmap的内存模型回到Java Heap层,此时它受JVM垃圾收集器的管理。不再强调recycle(),主要强调Bitmap的复用,有关Bitmap的复用我们在下面进一步讨论。

有趣的是:Android 8.0之后Bitmap内存模型又回到了Native层(不知道为什么)。

二、Bitmap的复用

1、inBitmap

Android在3.0之后BitmapFactory.Options引入了inBitmap属性,设置该属性之后解码图片时会尝试复用一张已经存在的Bitmap,避免了内存的回收以及重新申请的过程,对于内存的平滑使用显然进一步提升,性能表现也更佳。但是却有以下几点限制:

2、inBitmap的限制

1)声明可被复用的Bitmap必须设置inMutable为true;

2)Android4.4(API 19)之前只有格式为jpg、png,同等宽高(要求苛刻),inSampleSize为1的Bitmap才可以复用;

3)Android4.4(API 19)之前被复用的Bitmap的inPreferredConfig会覆盖待分配内存的Bitmap设置的inPreferredConfig;

4)Android4.4(API 19)之后被复用的Bitmap的内存必须大于需要申请内存的Bitmap的内存;

5)Android4.4(API 19)之前待加载Bitmap的Options.inSampleSize必须明确指定为1。

三、Bitmap如何复用

上面我们谈到了Bitmap的复用以及对复用的限制。

Google在官方文档中也给出了详尽的例子,具体参考:

https://developer.android.google.cn/topic/performance/graphics/manage-memory.html

1、inMutable:

可被复用的Bitmap的inMutable必须为true。

检查Bitmap是否能够复用的核心代码:

四、Bitmap占用内存

1、getByteCount()

返回可用于存储此位图像素的最小字节数。

getByteCount()方法是在API12加入的,代表存储Bitmap的像素需要的最少内存。API19开始getAllocationByteCount()方法代替了getByteCount()。

2、getAllocationByteCount()

返回用于存储此位图像素的已分配内存的大小。

API19之后,Bitmap加了一个Api:getAllocationByteCount();代表在内存中为Bitmap分配的内存大小。

3、getByteCount()与getAllocationByteCount()的区别

一般情况下两者返回是相等的,但是存在以下情况时则不然:

通过复用Bitmap来解码图片,如果被复用的Bitmap的内存比待分配内存的Bitmap大,那么getByteCount()表示新解码图片占用内存的大小(并非实际内存大小,实际大小是复用的那个Bitmap的大小),getAllocationByteCount()表示被复用Bitmap真实占用的内存大小(即mBuffer的长度)。

故:Api19之后应该首选使用getAllocationByteCount来获取当前Bitmap占用内存大小

4、Bitmap.Config(一个像素占用的内存大小)

Bitmap.Config用来描述图片的像素是怎么被存储的?

ARGB_8888:每个像素4字节, 共32位,默认设置。

Alpha_8:只保存透明度,共8位,1字节。

ARGB_4444:分别占用4位,共16位,2字节。

RGB_565:RGB分别占用5、6、5位,共16位,2字节。

5、计算Bitmap占用内存大小

这个大家可能都会知道:像素数量 * 一个像素占用内存大小 例:1920 * 1080 * 4 约等于7.9M。

那么只是加载一张Bitmap,它占用的内存 = width * height * 一个像素所占的内存。这种说法没有问题,但是在Android中情况可能就不一定了,因为我们忽略了Density(密度,下面我们来看下它对内存占用大小的影响)。

五、BitmapFactory(Density对内存占用大小的影响)

1、decodeResource()加载一张图片真正占用内存大小

上面说到Bitmap占用内存大小计算方式,但是在Android系统中存在特殊情况,这个情况就是像素密度带来的影响(Density)。首先我们先来看下源码:

BitmapFactory.java:

inDensity是图片所在资源目录对应的像素密度

inTargetDensity是目标设备的像素密度

BitmapFactory.cpp:

如果图片所在资源密度对应的像素密度 != 目标屏幕像素密度,会对解码图片计算一个缩放值

此处可以看出:BitmapFatcory.decodeResource()加载一张图片实际占用内存大小,占用的内存 = width * height * nTargetDensity/inDensity * nTargetDensity/inDensity * 一个像素所占的内存

六、总结

1、Bitmap内存模型

Android 3.0(API11)之前,Bitmap的像素数据存放在Native内存,而Bitmap对象本身则存放在Dalvik Heap中。而在Android3.0之后,Bitmap的像素数据也被放在了Java Heap中。

2、Bitmap内存回收

Android3.0之前主要强调Bitmap.recycle()显示释放Bitmap占用内存资源,在Android3.0之后主要强调Bitmap的复用。

3、Bitmap内存占用计算

在Android4.4之后推荐使用getAllocationByteCount()获取。

内存占用大小=width * height * nTargetDensity/inDensity * nTargetDensity/inDensity * 一个像素所占的内存

4、Bitmap复用

BitmapFactory.Options.inBitmap注意不同版本的限制问题

5、Glide

Google强烈推荐使用Glide来做Bitmap的加载。

荐:https://juejin.im/post/58c3b29761ff4b005d906730

荐:https://developer.android.google.cn/topic/performance/graphics/manage-memory.html

相关文章

网友评论

    本文标题:Android性能调优(5)—Bitmap内存模型

    本文链接:https://www.haomeiwen.com/subject/lrhmrftx.html