本文同步发布于公众号:移动开发那些事谈谈flutter的线程
刚接触flutter
的同学肯定会对fluter
所谓的单线程架构很蒙逼,因为这与我们学开发时,各种语言里的多线程的介绍有点出入,而且手机的CPU现在基本都是多核的,操作系统不可能同一时间只在处理一件事件的,
那么flutter
究竟是怎样实现 其所谓的单线程架构的呢? 在深入了解flutter
的线程架构前,我们先来看看flutter
使用的Dart
语言的线程模型Isolate
1 Isolate
Isolate
是Dart
中一种轻量级的执行单元,类似于其他语言的线程,但又比线程更轻量级。其在底层实际还是依赖于操作系统提供的OSThread
,与普通的线程的区别在于,Isolate
拥有独立的内存,而普通的线程则需要与其他线程共享内存。因此每个Isolate
相互独立,有自己单独内存空间(这个更接近于进程),不能在Isolate
之间共享内存。如果两个Isolate
之间需要通信,可以通过端口port
的方式进行数据的通信。
一个简单的Isolate
之间通信的代码为:
// 注册监听
var port = ReceivePort();
IsolateNameServer.registerPortWithName(
port.sendPort, "listen_name_demo");
port.listen((message) {
// 这里去处理接收到的信息
}
// 发送消息
// 名字需要与前面注册的一样
final SendPort? sendPort =
IsolateNameServer.lookupPortByName("listen_name_demo");
if (sendPort != null) {
// 里面的消息的结构,与注册那边保持一致就可(可理解为约定的协议格式)
sendPort.send([id, status.value, progress]);
return;
}
Isolate
之间通过消息传递进行通信的,但 Isolate
内部是怎样对消息进行分发处理的呢?
1.1 内部消息处理
Isolate
内部是通过事件循环和消息队列来实现内部的消息的分发处理的,每个Isolate
都会包含两个消息队列和一个事件循环
- Microtask queue: 微任务事件队列,微事件队列的优先级比普通事件队列高
- Event queue: 普通事件队列
- Event Loop: 事件循环,不断在从队列中获取事件进行分发处理,类似于
Android
原生的Looper
1.2 异步处理
前面我们了解到Isolate
是有消息队列的,但怎样给队列添加异步事件呢?答案是Future
一个简单的添加事件的代码为:
Future.delayed(const Duration(seconds: 3), () {
// 延迟3s后执行的异步操作
});
Future.microtask(() {
// 提交一个异步的微任务
});
而通过关键字Future
,async
和 await
的结合使用,我们可以把对应的方法变成一个异步的(也等于向普通事件队列里添加一个事件)
// async 说明这是一个异步的方法
Future<T> demoMethod() async {
// 具体的方法名
}
// 在使用的地方可以使用await 等待这个异步方法执行完(不加await说明不用等这个异步方法执行完)
await demoMehtod();
2 线程模型
Flutter
所谓的单线程架构是指如果我们不额外创建Isolate
的话,它的逻辑都是跑在一个叫做Root
的Isolate
上,那么这个Isolate
是哪里创建呢?
答案就是Embedder
(不知道Embedder
的同学,可以网上找一张Flutter
的架构图看看)。Embedder
是Flutter
的适配层,用于适配不同的操作系统,负责原生平台插件,
线程管理等功能。Embedder
提供了四个Task Runner
Platform Task Runner
:与平台的主线程相对应,用于处理与平台的交互;UI Task Runner
:用于执行 Dart 代码,处理 UI 渲染和帧更新(Root Isolate
就是运行在这里)。GPU Task Runner
:用于执行 GPU 绘制任务。IO Task Runner
:用于执行输入/输出任务,如文件读写、网络操作等;
2.1 Platform Task Runner
该Runner
运行所有在线程为原生的主线程(也就是我们经常说的UI
线程),主要用于:
- 与
Flutter
Engine
层进行交互 - 处理平台(
Android
,iOS
,Web
等)的消息
尽管是运行在原生的主线程,但在这个Runner
里做耗时的操作并不会导致整个应用的卡顿,不过一般也不建议在这里做比较耗时的操作;
2.2 UI Task Runner
该Runner
运行所有在线程为原生的子线程,主要用于:
- 执行
Dart
代码,也就是执行Root Isolate
的逻辑 - 执行渲染与处理
Vsync
信号,将Widget
转换生成Layer Tree
- 处理原生的
Plugin
消息 - 定时器,microtasks任务;
2.3 GPU Task Runner
该Runner
运行所有在线程为原生的子线程,主要用于:
- 执行
GPU
相关的指令; - 将
UI Task
生成的Layer Tree
转化为GPU
可执行的指令;
同样的,虽然是运行在原生的子线程里,但阻塞该线程会引起应用的卡顿,
2.4 IO Task Runner
该Runner
运行所有在线程为原生的子线程,主要用于:资源文件相关的处理,如处理图片数据;
3 总结
综上所述,Flutter
是通过以下几个部分来实现其线程的管理的:
Isolate
及其事件循环和消息队列- 异步处理的语法糖,如
async/await
和Future/Stream
Embedder
提供了四个Task Runner
4参考
公众号