概述
本文讨论Flutter的引擎架构。在整个flutter系统中,引擎位于承上启下的中间位置。上层承接flutter framewrok。下层是与原生平台对接的嵌入层(Embedder)。本文讨论Flutter的引擎架构。在整个flutter系统中,引擎位于承上启下的中间位置。上层承接flutter framewrok。下层是与原生平台对接的嵌入层(Embedder)
flutter系统概述
这里简单讲一下flutter系统的整体分层架构
- framework
提供直接面对开发者的组件页面的API,封装flutter的核心功能,如Widgets/Animation/Gertures等。
- C++ Engine(本文重点讨论的引擎)
提供高质量移动应用的轻量级运行时环境,包括
- 具有垃圾回收和面向对象语言的虚拟机
- 图形渲染库(使用Skia实现)
等。
实现了动画和图形、文字渲染、事件通知、通信管道等。并把他托管在shell中,shell就是嵌入层(Embedder)。shell实现和平台相关的代码。因为是和平台特性相关的,所以不同的平台有不同的shell。可以理解engine调用shell层的统一接口。不同平台做不同实现。shell为engine托管到平台的统一接口。
个人理解
shell就是Embedder,即引擎接入platform的中间层
引擎中的线程
引擎不会自己创建或管理线程。(虽然Engine中的Dart VM中存在线程池。但其中的线程也不是引擎自己创建的)。shell层负责创建和管理线程。engine要求shell提供四个task runner。engine只面对shell提供的task runner。往runner中丢任务执行。引擎不关心这些runner是否跑在一个线程中,但为了获得最好性能,嵌入环境应该给每一个 task runner 创建一个专用的线程。并保证在引擎生命周期内保持稳定。嵌入环境应该只在一个线程上执行对应 task runner 的任务(直到 Flutter 引擎被销毁)。
个人理解
完全实践了设计模式的原则:对不变封装,对变化开放。shell向engine提供的runner就是对不变封装,engine只与runner交互。shell(Android、iOS)对runner的实现,就是变化。交由不同平台的shell实现。有点像适配器模式。
主要的Task Runner
- Platform Task Runner
- UI Task Runner
- GPU Task Runner
- IO Task Runner
这里也是引擎架构的重点。明白这四个task的职责,也就明白了flutter引擎的运行。
Platform Task Runner
该runner从名字就能看出来,他通常跑在平台主线程的runner。Android上的主线程、iOS上的Foundation 引用的线程
但是分配给 task runner 对应线程的不论什么优先级的任务,这些任务的分配都是由嵌入环境来决定的。Flutter 引擎没有给这个线程赋予任何特殊的意义。
个人理解
该runner负责的工作内容是
- 嵌入层和引擎如果交互,需要通过该runner所在的线程进行交互,保证稳定性(嵌入层调引擎)
- 执行正在等待的平台消息(引擎调嵌入层)
也就是flutter引擎和平台的交互,通过该Runner。并且要一直通过该Runner 以保证稳定性。所以这也是为什么要选用平台的主线程,因为稳定。
UI Task Runner
UI Task Runner是引擎在根隔离(root isolate)上执行所有Dart代码的地方。也就是说所有flutter应用的Dark代码是运行在root isolate上,root isolate所有逻辑跑在UI Task Runner中。平台通常会为UI Task Runner配置单独的一个线程,因此UI Task Runner所在线程成为Dart线程。这也是为什么从APP应用层来看,flutter应用是单线程的。主要工作内容如下
- 执行所有root isolate上的dart代码,通常就是我们编写的应用代码
- 将开发人员编写的widget结构转换为Layer Tree。(渲染相关逻辑,在将渲染时会着重讨论)
- 处理Native Plugins消息、Timmer以及Microtasks。(在root isolate的event looper中)
我们类比Android应用,Android主线程负责的一项重要工作就是UI绘制,将XML的描述经过测量、布局、绘制过程最终提交Layer Tree到GPU。所以主线程是不能有耗时操作的。那么同样Dart线程也不能执行耗时操作。如果在Dart代码里做耗时操作不可避免,需要将代码移动到独立的Dart isolate中去(快捷写法compute)。开发者可以创建独立的isolate。独立isolate上的逻辑,最终会在Dart VM管理的线程池中执行,所以通过isolate可以实现应用层面的“多线程”。带引号是因为isolate和真正意义上的线程还是有区别的。isolate拥有独立的内存空间。而多线程是共享内存空间,所有存在锁的问题。
GPU Task Runner
GPU Task Runner主要用于执行设备GPU的指令。UI Task Runner创建的Layer Tree是跨平台的,它不关心到底由谁来完成绘制。GPU Task Runner负责将Layer Tree提供的信息转化为平台可执行的GPU指令。GPU Task Runner同时负责绘制所需要的GPU资源的管理。资源主要包括平台Framebuffer,Surface,Texture和Buffers等。
可以看到GPU Task Runner任务繁重,所以引擎建议shell为其创建单独的线程
IO Task Runner
上述提到的Runner对性能有着很强的要求(不能阻塞哦),
- platform task runner通常对应平台的主线程,长时间阻塞会触发平台的watchdog
- 阻塞UI Task runner或GPU Task runner将导致flutter应用卡顿
那么如读取硬盘的图片到内存等耗时的操作由谁负责呢?答案就是IO Task Runner.
IO task runner 的主要功能是从 asset store 读取压缩图像,并确保这些图像已准备好在 GPU task runner 上渲染。首先必须将其作为压缩数据(通常为PNG,JPEG等)从asset store读取,解压缩为GPU友好格式并传递给 GPU。这些操作时耗时的,如果在GPU Take runner执行将导致卡顿。所以引擎也建议shell(嵌入层)为其设立专用线程。
常见平台线程配置
Android
为每一个引擎实例的UI、GPU、IO Task runner配置专用线程。在平台上的所有引擎实例(应该就是多个flutter应用)共享主线程。
iOS
为每一个引擎实例的 UI,GPU 和 IO task runner 创建专用线程。在同一平台上所有的引擎实例共享平台线程和 task runner。
转载:https://blog.csdn.net/u012545728/article/details/116496464