Flutter官方架构图
Flutter的UI系统
flutter的ui系统主要理解三棵树:widgetTree、elementTree、renderTree。
Widget
Widget是immutable的,其主要结构如上图。
其中RenderObjectWidget是可渲染的,而StatelessWidget和StatefulWidget本质上只是Widget的组装容器,它们自己不参与渲染。
Widget的createElement方法会生成对应的element。
因为Widget是不可变的,所以flutter页面每次刷新时都会重建整个widget树。
Element
Element是Widget的抽象,其主要结构如上图。
Widget初始化时会通过createElement创建Element,Element持有Widget和RenderObject。下图代码简要描述了Element与RenderObject的关系:
BuildOwner会记录当前树结构中所有被标记为dirty的Element。
flutter页面刷新时ElementTree会更新但不会重建。
RenderObject
RenderObject才是被用于渲染的对象,其根据Widget的布局属性进行layout、paint。
flutter页面刷新时RenderTree会更新但不会重建。
线程模型
从四个线程说起:platform_taskRunner thread、ui_taskRunner thread、gpu_taskRunner thread、io_taskRunner thread.
在flutter中dart层是单线程模型,所有dart代码始终在其isolate的mutator线程上运行。
flutter引擎默认启动的isolate叫RootIsolate,而RootIsolate的mutator线程就是上面的ui_taskRunner thread。
所以说除了我们通过Isolate.spawn()执行并发的代码,其他dart代码都运行在ui_taskRunner thread。
platform_taskRunner thread是指android/ios应用进程的主线程,在安卓上则对应安卓的ui线程。
gpu_taskRunner thread是flutter用于处理渲染工作的线程,其主要工作对象是rasterizer,包括scene数据合成、光栅化以及opengl相关操作。
io_taskRunner thread主要用于处理图像数据编解码等异步耗时操作。
每个flutter引擎启动时都会创建自己的ui、gpu和io线程,但platform_taskRunner是当前进程的所有engine共用的,因为app进程的主线程始终是那一个。
isolate
dart中增加了一层isolate的概念,dart代码运行在一个isolate中,isolate与isolate之间内存不共享,它们只能通过port形式进行通信。
dart中使用关键字async/await实现异步操作,它们是以协程方式实现的。
而dart程序的并发则需要依赖多个isolate来实现。每个isolate创建时都会为其分配一条mutator线程用于运行dart代码。
isolate的七个阶段(不可逆):
*Unknown
初始状态,这是个内部状态,外部不会获取到处于这个阶段的isolate。
*Uninitialized
isolate刚被创建时的状态,也是内部状态。
*Initialized
初始化已完成,但尚未加载dart库,也是内部状态。
*LibrariesSetup
初始化已完成,dart代码也加载好了。
*Ready
已经可以运行dart代码了。
*Running
正在运行dart代码。
*Shutdown
isolate不再执行dart代码,等待回收了,也是内部状态。
Flutter应用启动流程
当应用初始化flutter后,默认会执行到lib/main.dart文件下的main函数(支持修改),在main函数中我们会调用runApp()启动flutter业务。
调用attachToRenderTree时首次构建起三棵树,而后更新树时只会更新被标记为dirty的节点。
渲染及刷新机制
以android分析,简化版的刷新流程如下图:
dart层将ElementTree中的element标记为dirty,然后通知引擎需要刷新页面,引擎通过jni去监听原生系统的垂直信号(Vsync),当下一个垂直信号到来时,引擎通知dart层更新ui树合成scene数据并取消dirty标记,引擎拿到scene数据后执行光栅化及帧合成,然后将帧数据渲染上屏。
渲染流程中ui线程的分工
animate、build、layout、compositing bits、paint、compositeFrame(将scene数据交给GPU thread)
渲染流程中gpu线程的分工
从scene获取layerTree和pipline用于光栅化和合成,然后通过skia向GPU提交数据渲染上屏。
光栅化是把绘制指令转换成对应的像素数据,合成是把各图层栅格化后的数据进行相关的叠加和特性处理。这个流程称为Graphics Pipeline。
PlatformChannel工作流解析
无论是MethodChannel还是EventChannel,其在C层的实现都是一样的。
channel中dart端逻辑始终在ui thread执行,而channel中原生侧逻辑始终运行在platform thread。
Flutter engine启动流程
主要关注的几个对象:engine、shell、DartVM、isolate、window。
shell:会创建一个RuntimeController对象。
window:是dart与c通信的主要媒介。每个RootIsolate拥有一个window对象。
isolate:可以理解为dart代码的运行时。
DartVM 解析
每个进程只有一个DartVM实例,进程内所有engine共用这个VM。
- 从settings中获取vm_snapshot、isolate_snapshot等数据
- 注册native方法供dart调用
- 创建vm_isolate对象
- 在启动engine时创建对应的isolate,并将dart代码中标记为"vm:entry-point"的方法关联给window。