转载

Fresco的内存机制

Fresco是Facebook出品的高性能图片加载库,号称在所有图片加载库中是效率、性能最好的。Fresco整个库还挺大的,还有native层。这里不对Fresco做深入分析,只关注Fresco在Android Bitmap的管理上采用了哪些黑科技

Android的内存区域

Java Heap(Dalvik Heap),这部分的内存区域是由Dalvik虚拟机管理,通过Java中 new 关键字来申请一块新内存。这块区域的内存是由GC直接管理,能够自动回收内存。这块内存的大小会受到系统限制,当内存超过APP最大可用内存时会OOM

Native Heap,这部分内存区域是在C++中申请的,它不受限于APP的最大可用内存限制,而只是受限于设备的物理可用内存限制。它的缺点在于没有自动回收机制,只能通过C++语法来释放申请的内存

Ashmem(Android匿名共享内存),这部分内存类似于Native内存区,但是它是受Android系统底层管理的,当Android系统在内存不足时,会回收Ashmem区域中状态是 unpin 的对象内存块,如果不希望对象被回收,可以通过 pin 来保护一个对象

Fresco的内存机制

Purgeable Bitmap

Ashmem一般在应用层中是无法直接访问的,除了几个特例之外。其中之一就是 decode bitmap ,我们可以通过设置 BitmapFactory.Optinons.inPurgeable = true 来创建一个 Purgeable Bitmap ,这样decode出来的bitmap是在Ashmem内存中,GC无法自动回收它。当该Bitmap在被使用时会被 pin 住,使用完之后就 unpin ,这样系统就可以在将来某一时间释放这部分内存。

如果一个 unpinned 的bitmap在之后又要被使用,系统会运行时又将它重新decode,但是这个decode操作是发生在UI线程中的有可能会造成掉帧现象,因此改做法已经被Google废弃掉,转为鼓励使用 inBitmap 来告知bitmap解码器去尝试使用已经存在的内存区域,新解码的bitmap会尝试去使用之前那张bitmap在heap中所占据的pixel data内存区域,而不是去问内存重新申请一块区域来存放bitmap。利用这种特性,即使是上千张的图片,也只会仅仅只需要占用屏幕所能够显示的图片数量的内存大小。

但是使用 inBitmap 需要注意几个限制条件:

  • 在SDK 11 -> 18之间,重用的bitmap大小必须是一致的,例如给inBitmap赋值的图片大小为100-100,那么新申请的bitmap必须也为100-100才能够被重用。从SDK 19开始,新申请的bitmap大小必须小于或者等于已经赋值过的bitmap大小。
  • 新申请的bitmap与旧的bitmap必须有相同的解码格式,例如大家都是8888的,如果前面的bitmap是8888,那么就不能支持4444与565格式的bitmap了。 我们可以创建一个包含多种典型可重用bitmap的对象池,这样后续的bitmap创建都能够找到合适的“模板”去进行重用。

Fresco的内存机制

Pin Bitmap

为了让inPurgeable的bitmap不被自动 unpinned ,可以通过使用jni函数 AndroidBitmap_lockPixels() 函数来强制 pin bitmap ,这样我们就可以在bitmap被使用时不会被系统自动 unpinned ,从而也就避免了 unpinned 的bitmap在重新被使用时又会被重新decode而引起的掉帧问题。同样的,Android也提供了 AndroidBitmap_unlockPixels() 来让bitmap重新变为 unpinned 状态,这样系统在内存不足时就可自动回收这部分内存

参考文献

Facebook tricks for image handling in Android

Introducing Fresco: A new image library for Android

Android系统匿名共享内存Ashmem(Anonymous Shared Memory)驱动程序源代码分析

欢迎大家拍砖

原创不易,欢迎转载,但还请注明出处:blog.waynell.com

原文  http://blog.waynell.com/2016/07/07/fresco-memory/
正文到此结束
Loading...