本文作者:赵旭阳
一、简介
我们在进行 Android 开发的时候,会通过创建一个 Handler 并调用其 sendMessage 或 Post 方法来进行异步消息调用,其背后涉及到了三个面试经常被问的类:Handler,Looper,MessageQueue,内部原理我想做过 Android 开发的基本都了解。
Event queue : 包含了所有的外部事件:I/O,鼠标点击,绘制,定时器,Dart isolate 的消息等,其实这块又根据消息的优先级细分成了两个队列,后面会有介绍。
Microtask queue :事件处理代码有时需要在当前 event 之后,且在下一个 event 之前做一些任务。
两种队列的消息处理流程大致如图所示:
dart 提供了 dart:async 库来对这两个队列进行操作,主要是如下两个API:
Future 类,创建一个定时器事件到 Event queue 尾部。
scheduleMicrotask() 方法,添加一个事件到 Microtask queue 尾部
下面将会分析这两个 API 背后的实现,涉及的是 flutter engine 的源码,可以参考官方的 wiki 来下载:
二、初始化
1. 创建 MessageLoop
在启动 Flutter 的时候,引擎会额外创建三个线程:UI Thread,IO Thread, GPU Thread,并为每个线程都创建一个 MessageLoop,之后各个线程就进入的消息循环的状态,等待新的消息来处理,具体流程如下:
ThreadHost thread_host_;
AndroidShellHolder::AndroidShellHolder(
flutter::Settings settings,
fml::jni::JavaObjectWeakGlobalRef java_object,
bool is_background_view)
: settings_(std::move(settings)), java_object_(java_object) {
......
if (is_background_view) {
thread_host_ = {thread_label, ThreadHost::Type::UI};
} else {
// 创建三个线程
thread_host_ = {thread_label, ThreadHost::Type::UI | ThreadHost::Type::GPU |
ThreadHost::Type::IO};
}
......
}
进一步分析 ThreadHost 的创建,最后来到了创建 Thread,每个 Thread 都会创建一个 MessageLoop 并获取其 TaskRunner,TaskRunner 是用来外部向 MessageLoop 中 Post Task 的。顺带说一下,在 Android 上 MessageLoop 的实现还是使用的系统自身的 Looper 机制,这里是通过 NDK 的 ALooper 相关接口来实现的。具体代码如下:
Thread::Thread(const std::string& name) : joined_(false) {
fml::AutoResetWaitableEvent latch;
fml::RefPtr<fml::TaskRunner> runner;
thread_ = std::make_unique<std::thread>([&latch, &runner, name]() -> void {
SetCurrentThreadName(name);
// 创建 MessageLoop
fml::MessageLoop::EnsureInitializedForCurrentThread();
auto& loop = MessageLoop::GetCurrent();
// 获取 TaskRunner
runner = loop.GetTaskRunner();
latch.Signal();
loop.Run();
});
latch.Wait();
task_runner_ = runner;
}
MessageLoop 创建好后,我们就可以通过 TaskRunner 向其发送 Task 了,这里需要注意 MessageLoop 执行的 Task 仅是一个 无参的闭包 。类似这样:
auto jni_exit_task([key = thread_destruct_key_]() {
FML_CHECK(pthread_setspecific(key, reinterpret_cast<void*>(1)) == 0);
});
thread_host_.ui_thread->GetTaskRunner()->PostTask(jni_exit_task);
2. 创建 Root Isolate
在 dart 语言中是没有线程的,而是使用类似于线程但相互之间不共享堆内存的 isolate,代表一个独立的 dart 程序执行环境。同样 Flutter 的 dart 代码是运行在一个叫 root isolate 的 isolate 中,下面简要列下 root isolate 的创建过程。
a.启动 dart vm
这个步骤的具体流程,大家可以顺着 /engine-v1.5.4/src/flutter/runtime/dart_vm.cc 中的 DartVM 构造方法去跟进分析。在 dart vm 启动过程中会创建 vm isolate 和 PortMap,这两个的具体作用下面有介绍。
b.创建 root isolate
root isolate 是在 UI 线程中创建的,具体流程见 /src/flutter/runtime/dart_isolate.cc 的 CreateRootIsolate 方法。由于 isolate 是对当前线程执行环境的一个抽象表示,所以其内部存储了很多信息,对于异步消息这块有四个关键的信息是需要注意的。
下面三个字段是定义在 dart vm 层的 Isolate 类中,具体见 /src/third_party/dart/runtime/vm/isolate.h。
main_port :可以看做该 isolate 的标识,是一个整数;
message_handler :顾名思义,用来管理每个 isolate 的 Event queue,其内部根据 message 的优先级将消息分为了两个队列:普通优先级的 queue 和 OOB 优先级的 oob_queue。了解 TCP 协议的应该了解 TCP 中有带外数据(即优先数据),isolate 的 OOB 优先级也是类似的意思,OOB 优先级的消息会被优先处理,目前看到有这么几种 OOB 消息:
// 这些主要和 isolate 的生命周期相关
kPauseMsg = 1,
kResumeMsg = 2,
kPingMsg = 3,
kKillMsg = 4,
kAddExitMsg = 5,
kDelExitMsg = 6,
kAddErrorMsg = 7,
kDelErrorMsg = 8,
kErrorFatalMsg = 9,
// 可以注意下 kLowMemoryMsg ,如果有大量 OOB 怀疑是内存不够了
kInterruptMsg = 10, // Break in the debugger.
kInternalKillMsg = 11, // Like kill, but does not run exit listeners, etc.
kLowMemoryMsg = 12, // Run compactor, etc.
kDrainServiceExtensionsMsg = 13, // Invoke pending service extensions
message_notify_callback :message_handler 收到消息后会调用该变量指向的函数去处理;
Flutter 引擎层会对 dart vm 的 isolate 实例做一层包装( DartIsolate 类),其内部定义了:
microtask_queue: 用来存储 microtask 消息,可见在 Flutter 引擎中,Microtask queue 并不是由 dart vm 层来管理的
前面已经说过 isolate 之间是不能直接互相访问,如图:
可以看出 isolate 之间是共享 vm isolate 的堆内存区域的,有点类似于操作系统的内核空间,vm isolate 的堆内存存储了 dart vm 内部的核心数据(