龙空技术网

从Flutter源码分析Image的加载及gif动画的实现

handler 81

前言:

当前你们对“加载动画 gif”大概比较珍视,看官们都想要知道一些“加载动画 gif”的相关内容。那么小编在网摘上搜集了一些对于“加载动画 gif””的相关内容,希望朋友们能喜欢,姐妹们快快来了解一下吧!

接着上文 从Flutter源码分析Image是如何被渲染出来的,从源码角度,对Flutter如何显示一个图片做进一步的分解

Image加载及解码

首先从 Image 的构造方法 network 开始,下面显示一张网络图片

Widget build(BuildContext context) {    return Image.network(        ";);  }

显示结果如下

跟踪 network 的定义,发现构造函数创建了一个 image 属性,并且创建了一个 NetworkImage 实例,而 NetworkImage 就是一个图片提供源,负责对 Image 的加载及解析,这个 NetworkImage 什么地方被调用的呢,接着看下面

我们再看 Image 的 didChangeDependencies 方法,didChangeDependencies 会在 initState 之后,build 之前执行,这里面调用了 _resolveImage 方法

再看 _resolveImage,调用了上面的 image 对象,然后调用 provider.resolve 方法进行解析

provider.resolve 函数里面又是一堆调用,但最终调用到了 NetworkImage 的 load 方法,然后 load 又调用了 NetworkImage 的 _loadAsync 方法,到此为止,才看到真正的图片加载及解码逻辑,我在里面增加了注释

在 _loadAsync 方法内,先进行网络请求去加载图片,然后把结果转换为 Uint8List 数据,最后对图片进行解码(解码最终调用的是 PaintingBinding.instance.instantiateImageCodec 方法,在UI线程)

再回到Image类内,在 _getListener 内监听了图片变化,当图片加载完毕后,回调 _handleImageFrame 方法,在此方法内,根据解析回来的 imageInfo 信息,调用 setState 进行界面刷新,再结合 从Flutter源码分析Image是如何被渲染出来的,图片信息最终会被形成canvas绘图指令,然后被渲染到屏幕上

图片的加载方式还有asset,file,memory等其他三种方式,分别使用了不同的解析类,相比network的网络加载方式,其他分别从本地文件,内存等地方加载数据,然后再进行解码,这里不再具体分析。

gif动画的显示

静态图片的加载和渲染流程已经清楚了,但gif图片是怎么形成动画的呢?带着这个疑问,我们继续分析

在 MultiFrameImageStreamCompleter 类里,找到 _decodeNextFrameAndSchedule 方法,首先获取了图片解码信息,拿到下一帧图片信息数据 _nextFrame (下面要用到),如果是静态图(只有一个图片信息),则直接显示,如果是动态图(有多帧),则执行 _scheduleAppFrame 方法

_scheduleAppFrame 内最终调用了 _handleAppFrame 方法,在这里,根据之前获得的 _nextFrame 信息, 从 _nextFrame.duration 获得动画间隔,从 _nextFrame.image 内获取下一帧的图片信息,然后传递给 Image 的 _handleImageFrame 方法进行展示,同时再根据动画间隔计算出下一次变化的时间,触发下一次绘制,获取下一帧图片及间隔,如此反复

总结

图片加载显示过程:

通过图片提供源 ImageProvider (NetworkImage 是网络图片源的一种实现,其他形式类似)的load方法,完成加载及解码过程,完成后把图片信息 ImageInfo 传递给 Image 的 _handleImageFrame 方法,调用 setState 刷新界面,再通过 canvas 的 drawImageRect 方法形成绘制指令绘制到屏幕上

gif动画过程:

在 _decodeNextFrameAndSchedule 方法内,首先获得图片解码信息,如果图片是多帧,则得到图片下一帧信息 (_nextFrame),再通过 _nextFrame.image 获得图片信息,通过 _nextFrame.duration 获得图片动画间隔,然后根据此时间间隔进行下一帧的获取及刷新,如此反复

Flutter Bug

在这里还发现一个Flutter的bug,如果使用 Image.asset 显示本地 gif,则会发现动画播放特别快,调试了一下,发现动画时间间隔是0,所以播放帧率就是当前app刷新的帧率,显得有点快,使用 Image.network 则正常,等待官方修复。

标签: #加载动画 gif