背景
图片展示是应用程序中最常见的功能,在android中有一些很经典的库来处理图片的缓存,广泛应用例如ImageLoader,Glide,Frescho,flutter官方提供了Image的widget来展示图片,Image内部封装了具体文件数据如何获取,以及数据怎么缓存;
image widget分析
Image 是一个StateFullWidget,除了默认构造函数外,还提供了命名构造函数,覆盖了常见的文件获取途径:
Image.network
Image.file
Image.asset
Image.memory
_ImageState的didChangeDependencies函数会对图片进行解析,
_resolveImage函数主要有两个步骤:
1、解析图片,返回ImageStream对象;
2、对ImageStream添加监听器,当图片解析完成后,通过setState刷新Widget;
所以关键步骤在步骤1,实际上是调用成员变量image的resolve方法,这里的image实际上是ImageProvier:
ImageProvier是一个抽象类,image是在构造函数里初始化的,例如Image.file:
本文就以FileImage作为目标分析一个本地文件图片的解析过程:
FileImage类并没有复写resolve方法,实现是在父类ImageProvier中,该法完成了两个步骤:
1、生成ImageStreamCompleter对象:
2、将completer设置到ImageStream中去,返回这个ImageStream;
所以关键步骤是1,这里是调了PaintingBinding.instance
.imageCache的putIfAbsent方法,正是在这个方法里面体现了image缓存策略,分为四个步骤
1、从_pendingImages获取图片:
如果result不空,说明这个图片还在解析中,直接返回这个解析中的 result;
2、从_cache查找是否存在,如果存在则返回缓存对象:
3、执行真正的解析:
loader是ImageProvider里的抽象函数,具体实现在各个子类中,例如在FileImage中:
返回的是一个MultiFrameImageStreamCompleter的对象,说明是支持Gif的。
4、构造一个_PendingImage对象,存入_pendingImages,就是步骤1获取的数据,直到图片解析完成后首先执行_pendingImages的监听器,在监听器内部实现图片数据进缓存,和check缓存池size;
下面看MultiFrameImageStreamCompleter,在构造函数内部执行真正的Load操作:
Future执行完后的回调_handleCodecReady 最终进入_decodeNextFrameAndSchedule,
对于单帧图片的文件解析完就返回给了Image的监听器,对于多帧图片,除了通知Image的监听器外,还会继续解析下一帧;
这个样整个FileImage的解析过程就走完了。
总结:
1、ImageCache是一个比较简单的Lru的缓存,缓存的数据都在内存级别,没有做持久化的数据缓存;
2、ImageCache提供了Evict函数,用于清理缓存,这是优化内存的一种切入点;
对于gif图的解析需要注意的是:
1、如果只需要展示首帧图片的话,需要再解析完成后,清理removeListener.
2、退出界面后要调用evict方法,来清理缓存,否则下次进来会看到的是下一帧的图片。