一、Flutter的启动流程
1、Flutter启动初始化
在安卓平台上启动App时,会先创建应用进程,然后创建FlutterApplication并执行其onCreate方法。在FlutterApplication的onCreate方法中,会执行Flutter相关的初始化,主要包括Flutter编译产物相关名称和路径的初始化、处理平台侧和Flutter侧Vsync信号的同步、加载引擎代码libflutter.so库、为Flutter创建存储目录等操作。
VsyncWaiter的初始化方法中,创建了原生平台层和Flutter引擎层同步Vsync信号的代理对象asyncWaitForVsyncDelegate。当Flutter侧需要刷新UI界面时,通过asyncWaitForVsyncDelegate和FlutterJNI将原生平台收到的Vsync回调转发给自身,然后在触发渲染流程更新界面。
下图是启动初始化的调用流程图:
2、FlutterActivity引擎获取
在FlutterActivity的onCreate方法中,通过调用delegate的onAttach方法获取Flutter引擎。引擎的获取主要分为三个步骤:
- 首先,从FlutterEngineCache中尝试获取已缓存的FlutterEngine
- 如果缓存中没有可用的FlutterEngine,则从provideFlutterEngine方法中获取自定义的FlutterEngine
- 如果以上两种方式都无法获取到FlutterEngine,那么将通过FlutterEngineGroup创建新的FlutterEngine
下图是FlutterActivity引擎获取的调用流程图:
3、FlutterEngineGroup创建引擎
Flutter引擎的创建有两种方式,一种是直接创建,一种是通过EngineGroup创建。通过EngineGroup方式创建的引擎之间能够共享部分资源(例如 GPU 上下文、字体度量和隔离线程的快照),从而加快首次渲染的速度、降低延迟并降低内存占用,每增加一个引擎只会增加约 180K 的内存占用。
FlutterEngineGroup创建引擎时,如果还没有创建过引擎,那么就会直接创建第一个引擎。如果已经有存在的引擎了,那么就会通过spawn部分资源共享的方式创建新引擎。
创建引擎时,会创建Dart侧的执行入口DartEntrypoint,如果提供自定义入口函数了,那DartEntrypoint中包含的就是自定义的入口函数名。如果没有提供自定义的入口函数,那么DartEntrypoint中包含的就是默认的入口函数名“main”。
创建引擎时,还会注册平台消息handler,创建平台系统channel,用于Flutter端和原生端通信。
创建引擎时,还会初始化Flutter环境、DartVM、FlutterEngine,关联FlutterJNI和Engine便于平台和引擎通信。创建FlutterRender对象,最终会执行Dart侧的入口方法,执行Dart代码。
下图是通过EngineGroup方式创建引擎的调用流程图:
二、Flutter的渲染流程
1、FlutterActivity视图渲染
FlutterActivity通过FlutterView呈现Flutter画面,并实现渲染显示。FlutterView继承自FrameLayout,它本身也是一个View。实际上,FlutterView的渲染是通过其内部的FlutterSurfaceView或FlutterTextureView(两者都是RenderSurface的实现类,不考虑FlutterImageView)来真正进行的。选择使用FlutterSurfaceView还是FlutterTextureView是根据RenderMode来决定的。
在FlutterView与Flutter引擎建立关联时,FlutterSurfaceView或FlutterTextureView也会与引擎的FlutterRenderer建立关联。Dart侧编写的Widget渲染树会交给引擎进行渲染,而FlutterRenderer负责将Flutter引擎的画面数据渲染到FlutterSurfaceView或FlutterTextureView上。渲染数据是通过FlutterJNI进行Engine与Android层之间传递的,最终将画面显示到原生平台视图中。
下图是FlutterActivity视图渲染的调用流程图:
2、Dart树渲染流程
Dart程序的执行是从入口方法(默认为main方法)开始的,接着会调用WidgetsBinding的scheduleAttachRootWidget方法,根据Dart业务代码构建出三棵树。然后,通过调用SchedulerBinding的scheduleWarmUpFrame方法启动渲染流程。最终,在RendererBinding的drawFrame方法中,将会调用PipelineOwner进行布局、合成、绘制等一系列操作。最后,通过调用Skia将视图绘制到屏幕上。
当Widget的状态需要变更时,我们可以调用State.setState来触发这个过程。首先,将会对发生变化的元素进行标记,并将其添加到_dirtyElements这个脏元素列表中。然后,通知Flutter引擎需要刷新用户界面。在Flutter引擎层,会向平台层注册Vsync信号回调。一旦Vsync信号到达,就会开始触发渲染流程。首先会对_dirtyElements中的元素进行重建和更新,生成更新后的渲染树。最终,在RendererBinding的drawFrame方法中,将会调用PipelineOwner进行布局、合成、绘制等一系列操作。最后,通过调用Skia将视图绘制到屏幕上。
下图是Dart树渲染的调用流程图:
【个人微信公众号】
专注于Android开发领域技术分享