图片加载利器——fresco

众所周知,在移动设备上运行的应用,其运行环境提供的运行空间和资源有限,因此对应用性能有极高的要求,像图片这样消耗内存和cpu资源的大户,一直是移动开发领域中的一个热点话题。幸运的是已经有很多无私奉献的人为我们提供了现成的图片框架。比如Universal Image Loader,Picasso,Glide,Volley等。以及今天要介绍的——Fresco。Fresco是Facebook开源的一款图片显示框架。

高效的内存管理

首先,Android上运行的App的java堆内存都是被严格限制的。每个对象都是使用new在堆内存中实例化。内存有垃圾回收机制,所以当App不再使用内存时,系统会在合适的时机自动把这块内存回收。
但是,内存回收的过程,不仅对垃圾进行了回收,还把正在运行的Android程序也终止了。这是用户在使用时感觉卡顿或假死的最重要原因。相比之下,Native对是由C++程序的new分配的。在Native堆里有更多可用内存。App只被设备的物理内存所限制,而不会被垃圾回收机制所影响。但是C++程序员必须自己回收所分配的每一块内存,否则会导致内存泄露,最终导致程序崩溃。
在Android里,有另外一种内存区域,叫做Ashmem(Anonymous Shared Memory),既匿名共享内存,具体可以参考老罗的
这篇文章
它操作起来更像Native堆,但是也有额外的系统调用。Android在操作Ashmem堆时,会把该堆中存有数据的内存区域从Ashmem对中抽取出来,而不是把它释放掉,这是一种弱内存释放模式,被抽取出来的这部分内存只有当系统真正需要更多的内存时才会被释放,当Android把被抽取出来的这部分内存放回Ashmem对,只要被抽取的内存空间没有被释放,之前的数据就会恢复到相应的位置。
Fresco在缓存bitmap对象时,在5.0以下的系统,会把缓存放在Ashmem中,而5.0及其以上的系统,相比之下起内存管理有了很大的改进,所以bitmap的缓存是位于java堆内存中。当应用在后台运行时,该内存会被清空。
相比于其他的框架,fresco把缓存分成三块:1.bitmap缓存;2.未解码图片的内存缓存。从该缓存渠道的图片在使用之前,需要先进行解码。3.磁盘缓存。磁盘缓存存储的也是未解码的原始压缩格式的图片,使用前同样要经过解码等处理。
显示一张图片的流程如下:
图片显示流程

拥抱stream

web上的渐进式JPEG图片已经成为一种使用习惯,在打开文件过程中,会先显示整个图片的模糊轮廓,让用户知道正在加载的图片大概是什么。Fresco把这个特效带到了Android上,它定义了一个类似于java中Future的DataSource。不同之处在于Future返回的是一个结果,而DataSource返回的一个完整而连续的显示结果,这样就实现了“渐进”的效果。

动画完全支持

动画大多以Gif和WebP的格式存在,如果支持这些格式,就需要面临一个新的挑战。因为每一个动画都由不知一张图片组成,你需要解码每一张图片,存储在内存里,然后显示出来,对于较大的动画,把每一帧放在内存是不可行的。
Fresco建立了AnimatedDrawable,一个强大的可以呈现动画的drawable,同时支持Gif和WebP格式。AndroidmatedDrawable实现了标准的Android Animatable接口,所以调用者可以随意启动和停止动画。

忘记ImageView

显示图片一般使用ImageView,当图片准备好显示时,使用ImageView代替占位图。然而,改变View会让Android强制刷新整个布局,当用户滑动时,这个效果比较尴尬。明智的做法是使用Drawables,他可以迅速的被替换。
Fresco创建了Drawee。这是一个类似于MVC架构的图片显示框架,在Fresco中有个专门的名称——DraweeHierarchy。他被实现为Drawables的一个层,对于底层图片而言,每个层都有特定的功能——成像、渐变或者缩放。
DraweeControllers通过管道的方式连接到图像上。他们控制着DraweeHierarchy的实际操作,比如站位图片,出错时的图片,成功时的图片。DeaweeView会监听Android的View不再显示在屏幕上的系统事件。当图片离开屏幕时,DraweeView可以告诉DraweeController关闭图片资源。这可以避免内存泄露。此外,如果它已经不在屏幕范围内的话,控制器会告诉图片管道取消网络请求,这在listview中显示图片的情况中是一个及其明显的优化。