美文网首页性能优化—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