探索 Flutter 异步消息的实现

本文作者:赵旭阳

一、简介

我们在进行 Android 开发的时候,会通过创建一个 Handler 并调用其 sendMessage  或 Post 方法来进行异步消息调用,其背后涉及到了三个面试经常被问的类:Handler,Looper,MessageQueue,内部原理我想做过 Android 开发的基本都了解。

  1. Event queue : 包含了所有的外部事件:I/O,鼠标点击,绘制,定时器,Dart isolate 的消息等,其实这块又根据消息的优先级细分成了两个队列,后面会有介绍。

  2. Microtask queue :事件处理代码有时需要在当前 event 之后,且在下一个 event 之前做一些任务。

  • 两种队列的消息处理流程大致如图所示:

640?wx_fmt=png


dart 提供了 dart:async 库来对这两个队列进行操作,主要是如下两个API:

  1. Future 类,创建一个定时器事件到 Event queue 尾部。

  2. 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 之间是不能直接互相访问,如图:

640?wx_fmt=png

可以看出 isolate 之间是共享 vm isolate 的堆内存区域的,有点类似于操作系统的内核空间,vm isolate 的堆内存存储了 dart vm 内部的核心数据(

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

singwhatiwanna

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值