基于Flutter 1.5,从源码视角来深入剖析flutter消息处理机制,相关源码目录见文末附录
一、概述
在深入理解Flutter引擎启动 已经介绍了引擎启动阶段会创建AndroidShellHolder对象,在该过程会执行ThreadHost初始化,MessageLoop便是在这个阶段启动的。
1.1 消息流程图
该过程主要工作:创建线程,并给每个线程中创建相应的MessageLoop,对于Android平台创建的是MessageLoopAndroid,同时还会创建TaskRunner,之后便进入到相应MessageLoop的run()方法。
1.2 MessageLoop类图
图解:
- Thread和MessageLoop类都有成员变量记录着TaskRunner类;
- MessageLoopImpl类在Android系统的实现子类为MessageLoopAndroid;
二、MessageLoop启动
引擎启动过程会创建UI/GPU/IO这3个线程,代码如下。
thread_host_ = {thread_label, ThreadHost::Type::UI
| ThreadHost::Type::GPU
| ThreadHost::Type::IO};
2.1 ThreadHost初始化
[-> flutter/shell/common/thread_host.cc]
ThreadHost::ThreadHost(std::string name_prefix, uint64_t mask) {
if (mask & ThreadHost::Type::Platform) {
platform_thread = std::make_unique<fml::Thread>(name_prefix + ".platform");
}
if (mask & ThreadHost::Type::UI) {
//创建线程 [见小节2.2]
ui_thread = std::make_unique<fml::Thread>(name_prefix + ".ui");
}
if (mask & ThreadHost::Type::GPU) {
gpu_thread = std::make_unique<fml::Thread>(name_prefix + ".gpu");
}
if (mask & ThreadHost::Type::IO) {
io_thread = std::make_unique<fml::Thread>(name_prefix + ".io");
}
}
根据传递的参数,可知首次创建AndroidShellHolder实例的过程,会创建3个线程名为1.ui, 1.gpu, 1.io。
2.2 Thread初始化
[-> flutter/fml/thread.cc]
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); //设置线程名
fml::MessageLoop::EnsureInitializedForCurrentThread(); //[见小节2.3]
//从ThreadLocal中获取MessageLoop指针
auto& loop = MessageLoop::GetCurrent();
runner = loop.GetTaskRunner();
latch.Signal();
loop.Run(); //运行 [见小节2.8]
});
latch.Wait();
task_runner_ = runner;
}
Thread线程对象会有两个重要的成员变量:
- thread_: 类型为unique_ptr<std::thread>
- task_runner_: 类型为RefPtr<fml::TaskRunner>
2.3 EnsureInitializedForCurrentThread
[-> flutter/fml/message_loop.cc]
FML_THREAD_LOCAL ThreadLocal tls_message_loop([](intptr_t value) {
delete reinterpret_cast<MessageLoop*>(value);
});
void MessageLoop::EnsureInitializedForCurrentThread() {
if (tls_message_loop.Get() != 0) {
return; //保证只初始化一次
}
//创建MessageLoop,并保持在tls_message_loop [见小节2.4]
tls_message_loop.Set(reinterpret_cast<intptr_t>(new MessageLoop()));
}
创建MessageLoop对象保存在ThreadLocal类型的tls_message_loop变量中。
2.4 MessageLoop初始化
[-> flutter/fml/message_loop.cc]
MessageLoop::MessageLoop()
//[见小节2.5]
: loop_(MessageLoopImpl::Create()),
//[见小节2.7]
task_runner_(fml::MakeRefCounted<fml::TaskRunner>(loop_)) {
}
创建MessageLoopAndroid对象和TaskRunner对象,并保持在当前的MessageLoop对象的成员变量。
2.5 MessageLoopImpl::Create
[-> flutter/fml/message_loop_impl.cc]
fml::RefPtr<MessageLoopImpl> MessageLoopImpl::Create() {
#if OS_MACOSX
return fml::MakeRefCounted<MessageLoopDarwin>();
#elif OS_ANDROID
return fml::MakeRefCounted<MessageLoopAndroid>(); //[见小节2.6]
#elif OS_LINUX
return fml::MakeRefCounted<MessageLoopLinux>();
#elif OS_WIN
return fml::MakeRefCounted<MessageLoopWin>();
#else
return nullptr;
#endif
}
针对Android平台,则MessageLoopImpl的实例为MessageLoopAndroid对象。
2.6 MessageLoopAndroid初始化
[-> flutter/fml/platform/android/message_loop_android.cc]
MessageLoopAndroid::MessageLoopAndroid()
: looper_(AcquireLooperForThread()), //[见小节2.6.1]
timer_fd_(::timerfd_create(kClockType, TFD_NONBLOCK | TFD_CLOEXEC)),
running_(false) {
static const int kWakeEvents = ALOOPER_EVENT_INPUT;
ALooper_callbackFunc read_event_fd = [](int, int events, void* data) -> int {
if (events & kWakeEvents) {
//不断收到回调
reinterpret_cast<MessageLoopAndroid*>(data)->OnEventFired();
}
return 1;
};
int add_result = ::ALooper_addFd(looper_.get(), // looper
timer_fd_.get(), // fd
ALOOPER_POLL_CALLBACK, // ident
kWakeEvents, // events
read_event_fd, // callback
this // baton
);
}
2.6.1 AcquireLooperForThread
[-> flutter/fml/platform/android/message_loop_android.cc]
static ALooper* AcquireLooperForThread() {
//获取当前线程的loop
ALooper* looper = ALooper_forThread();
if (looper == nullptr) {
// 当前线程没有配置looper,则创建一个loop
looper = ALooper_prepare(0);
}
// 该线程已经有looper,则获取该loop的引用
ALooper_acquire(looper);
return looper;
}
此处是通过android中的ndk工具实现loop消息机制,对于1.ui, 1.gpu, 1.io线程会创建native的loop,对于main线程会复用Android原生的native loop。
- ALooper_forThread:获取当前线程的loop,对应Looper::getForThread(),通过该线程key向TLS来查询是否存在已创建的c++层的loop
- ALooper_prepare:创建新的loop,对应Looper::prepare(),在TLS中记录着该线程为key,loop为value的数据。
- ALooper_acquire: 获取loop的引用,对应looper->incStrong(),也就是将引用计数加1;
2.6.2 timerfd_create
[-> flutter/fml/platform/linux/timerfd.cc]
int timerfd_create(int clockid, int flags) {
return syscall(__NR_timerfd_create, clockid, flags);
}
通过系统调用来创建timerfd
2.7 TaskRunner初始化
[-> flutter/fml/task_runner.cc]
TaskRunner::TaskRunner(fml::RefPtr<MessageLoopImpl> loop)
: loop_(std::move(loop)) {}
执行到这里,便完成了MessageLoop的初始化,回到小节2.2,接下来执行Run()方法。
2.8 MessageLoopAndroid::Run
[-> flutter/fml/platform/android/message_loop_android.cc]
void MessageLoopAndroid::Run() {
running_ = true;
while (running_) {
int result = ::ALooper_pollOnce(-1, // infinite timeout
nullptr, // out fd,
nullptr, // out events,
nullptr // out data
);
if (result == ALOOPER_POLL_TIMEOUT || result == ALOOPER_POLL_ERROR) {
// 处理使用ALooper API终止循环的情况
running_ = false;
}
}
}
该线程处于pollOnce轮询等待的状态。
三、生产Dart层Microtask
对于Dart框架层可调用scheduleMicrotask()方法是用于向Microtask Queue中添加task,接着从该方法说起。
3.1 scheduleMicrotask
[-> third_party/dart/sdk/lib/async/schedule_microtask.dart]
void scheduleMicrotask(void callback()) {
_Zone currentZone = Zone.current;
if (identical(_rootZone, currentZone)) {
//rootZone [见小节3.2]
_rootScheduleMicrotask(null, null, _rootZone, callback);
return;
}
_ZoneFunction implementation = currentZone._scheduleMicrotask;
if (identical(_rootZone, implementation.zone) &&
_rootZone.inSameErrorZone(currentZone)) {
_rootScheduleMicrotask(
null, null, currentZone, currentZone.registerCallback(callback));
return;
}
Zone.current.scheduleMicrotask(Zone.current.bindCallbackGuarded(callback));
}
此处以rootZone为例来展开说明
3.2 _rootScheduleMicrotask
[-> third_party/dart/sdk/lib/async/zone.dart]
void _rootScheduleMicrotask(
Zone self, ZoneDelegate parent, Zone zone, void f()) {
if (!identical(_rootZone, zone)) {
bool hasErrorHandler = !_rootZone.inSameErrorZone(zone);
if (hasErrorHandler) {
f = zone.bindCallbackGuarded(f);
} else {
f = zone.bindCallback(f);
}
zone = _rootZone;
}
_scheduleAsyncCallback(f); // [见小节3.3]
}
3.3 _scheduleAsyncCallback
[-> third_party/dart/sdk/lib/async/schedule_microtask.dart]
void _scheduleAsyncCallback(_AsyncCallback callback) {
_AsyncCallbackEntry newEntry = new _AsyncCallbackEntry(callback);
if (_nextCallback == null) {
//当链表为空,则设置当前回调Entry为链表的头部
_nextCallback = _lastCallback = newEntry;
if (!_isInCallbackLoop) {
// [见小节3.4]
_AsyncRun._scheduleImmediate(_startMicrotaskLoop);
}
} else {
//当链表不为空,则插入链表的尾部
_lastCallback.next = newEntry;
_lastCallback = newEntry;
}
}
class _AsyncRun {
//[见小节3.3.1]
external static void _scheduleImmediate(void callback());
}
采用一个单向链表来记录callback回调实体,其中_nextCallback是链表头部,_lastCallback是链表尾部;此处的_scheduleImmediate最终会调用到dart runtime中的schedule_microtask_patch.dart文件。
3.4 _scheduleImmediate
[-> third_party/dart/runtime/lib/schedule_microtask_patch.dart]
class _AsyncRun {
static void _scheduleImmediate(void callback()) {
_ScheduleImmediate._closure(callback); //[见小节3.4.1]
}
}
typedef void _ScheduleImmediateClosure(void callback());
class _ScheduleImmediate {
static _ScheduleImmediateClosure _closure;
}
对于_ScheduleImmediate._closure的赋值过程,是在引擎启动的过程。
3.4.1 InitDartAsync
在引擎启动过程会创建RootIsolate,有如下调用链:
DartIsolate::CreateRootIsolate
DartIsolate::CreateDartVMAndEmbedderObjectPair
DartIsolate::LoadLibraries
DartRuntimeHooks::Install
flutter::InitDartAsync
再来看看InitDartAsync的实现过程,如下所示。
[-> flutter/lib/ui/dart_runtime_hooks.cc]
static void InitDartAsync(Dart_Handle builtin_library, bool is_ui_isolate) {
Dart_Handle schedule_microtask;
if (is_ui_isolate) {
schedule_microtask = GetFunction(builtin_library, "_getScheduleMicrotaskClosure");
} else {
...
}
Dart_Handle async_library = Dart_LookupLibrary(ToDart("dart:async"));
Dart_Handle set_schedule_microtask = ToDart("_setScheduleImmediateClosure");
Dart_Handle result = Dart_Invoke(async_library, set_schedule_microtask, 1,
&schedule_microtask);
PropagateIfError(result);
}
可见此处会调用schedule_microtask_patch.dart的_setScheduleImmediateClosure()。
@pragma("vm:entry-point", "call")
void _setScheduleImmediateClosure(_ScheduleImmediateClosure closure) {
_ScheduleImmediate._closure = closure;
}
可见,经过InitDartAsync()方法,将_getScheduleMicrotaskClosure赋值给_closure。那么前面小节[3.3.1]便开始执行_getScheduleMicrotaskClosure()方法。
3.4.2 _getScheduleMicrotaskClosure
[-> flutter/lib/ui/natives.dart]
@pragma('vm:entry-point')
Function _getScheduleMicrotaskClosure() => _scheduleMicrotask;
void _scheduleMicrotask(void callback()) native 'ScheduleMicrotask'; //[见小节3.5]
3.5 ScheduleMicrotask
[-> flutter/lib/ui/dart_runtime_hooks.cc]
void ScheduleMicrotask(Dart_NativeArguments args) {
Dart_Handle closure = Dart_GetNativeArgument(args, 0);
UIDartState::Current()->ScheduleMicrotask(closure); //[见小节3.6]
}
此处的参数closure便是[小节3.3]中的_startMicrotaskLoop。
3.6 UIDartState::ScheduleMicrotask
[-> flutter/lib/ui/ui_dart_state.cc]
void UIDartState::ScheduleMicrotask(Dart_Handle closure) {
microtask_queue_.ScheduleMicrotask(closure); //[见小节3.7]
}
3.7 DartMicrotaskQueue::ScheduleMicrotask
[-> third_party/tonic/dart_microtask_queue.cc]
void DartMicrotaskQueue::ScheduleMicrotask(Dart_Handle callback) {
queue_.emplace_back(DartState::Current(), callback);
}
queue_的类型为MicrotaskQueue,记录着所有的DartState所对应的Dart_Handle回调方法的队列。由前面可知,此处callback为_startMicrotaskLoop()方法。
对于Microtask任务:
- UIDartState::ScheduleMicrotask会向队列中添加Microtask任务,添加后并不会唤醒目标线程;
- UIDartState::FlushMicrotasksNow则会一次性消费掉全部的Microtask微任务。
四、生产引擎层Task
在Flutter引擎层会看到有不少TaskRunner::PostTask()的使用地方,如下所示。
void Animator::RequestFrame(bool regenerate_layer_tree) {
...
task_runners_.GetUITaskRunner()->PostTask([self = weak_factory_.GetWeakPtr(),
frame_number = frame_number_]() {
self->AwaitVSync();
});
}
4.1 TaskRunner::PostTask
[-> flutter/fml/task_runner.cc]
void TaskRunner::PostTask(fml::closure task) {
//[见小节4.2]
loop_->PostTask(std::move(task), fml::TimePoint::Now());
}
void TaskRunner::PostTaskForTime(fml::closure task,
fml::TimePoint target_time) {
loop_->PostTask(std::move(task), target_time);
}
void TaskRunner::PostDelayedTask(fml::closure task, fml::TimeDelta delay) {
loop_->PostTask(std::move(task), fml::TimePoint::Now() + delay);
}
方法说明;
- PostTask:立刻执行的任务;
- PostTaskForTime:指定时间来执行的任务;
- PostDelayedTask:延迟一段时间来执行的任务;
4.2 MessageLoopImpl::PostTask
[-> flutter/fml/message_loop_impl.cc]
void MessageLoopImpl::PostTask(fml::closure task, fml::TimePoint target_time) {
RegisterTask(task, target_time);
}
4.3 MessageLoopImpl::RegisterTask
[-> flutter/fml/message_loop_impl.cc]
void MessageLoopImpl::RegisterTask(fml::closure task,
fml::TimePoint target_time) {
if (terminated_) { //消息loop已终止则结束执行
return;
}
std::lock_guard<std::mutex> lock(delayed_tasks_mutex_);
//将该任务放入delayed_tasks_队列
delayed_tasks_.push({++order_, std::move(task), target_time});
//在指定的时间会唤醒delayed_tasks_所在的线程[见小节4.3.1]
WakeUp(delayed_tasks_.top().target_time);
}
delayed_tasks_的数据类型为DelayedTaskQueue,也就是std::priority_queue<DelayedTask, std::deque
struct DelayedTaskCompare {
bool operator()(const DelayedTask& a, const DelayedTask& b) {
return a.target_time == b.target_time ? a.order > b.order
: a.target_time > b.target_time;
}
};
4.3.1 WakeUp
[-> flutter/fml/platform/android/message_loop_android.cc]
void MessageLoopAndroid::WakeUp(fml::TimePoint time_point) {
bool result = TimerRearm(timer_fd_.get(), time_point); //[见小节4.3.2]
}
每次向延迟任务队列放入消息的同时,会设置目标时间来唤醒的操作。
4.3.2 TimerRearm
[-> flutter/fml/platform/linux/timerfd.cc]
bool TimerRearm(int fd, fml::TimePoint time_point) {
const uint64_t nano_secs = time_point.ToEpochDelta().ToNanoseconds();
struct itimerspec spec = {};
spec.it_value.tv_sec = (time_t)(nano_secs / NSEC_PER_SEC);
spec.it_value.tv_nsec = nano_secs % NSEC_PER_SEC;
spec.it_interval = spec.it_value;
//设置定时唤醒
int result = ::timerfd_settime(fd, TFD_TIMER_ABSTIME, &spec, nullptr);
return result == 0;
}
通过系统调用__NR_timerfd_settime来设置定时唤醒。
五、消费任务
任务消费有两个时机:
- 帧开始绘制过程Window::BeginFrame(),会调用UIDartState::FlushMicrotasksNow()会消费Microtask;
- MessageLoop的消息循环过程,会同时消费Microtask和引擎层Task;
5.1 添加TaskObserver
5.1.1 UIDartState初始化
[-> flutter/lib/ui/ui_dart_state.cc]
UIDartState::UIDartState(...) {
//UIDartState对象创建时添加task observer [见小节5.1.2]
AddOrRemoveTaskObserver(true);
}
UIDartState::~UIDartState() {
//UIDartState对象销毁时移除task observer
AddOrRemoveTaskObserver(false ;
}
5.1.2 AddOrRemoveTaskObserver
[-> flutter/lib/ui/ui_dart_state.cc]
void UIDartState::AddOrRemoveTaskObserver(bool add) {
auto task_runner = task_runners_.GetUITaskRunner();
...
if (add) {
//[见小节5.1.4]
add_callback_(reinterpret_cast<intptr_t>(this),
[this]() { this->FlushMicrotasksNow(); });
} else {
remove_callback_(reinterpret_cast<intptr_t>(this));
}
}
引擎启动的DartIsolate初始化时,会创建一个UIDartState对象,则向MessageLoop中添加一个TaskObserver,其中以当前UIDartState对象为key,以FlushMicrotasksNow()方法为value;当UIDartState对象销毁时移除task observer。 再来看看add_callback_的赋值过程,如下所示。
5.1.3 FlutterMain::Init
在Flutter引擎启动时执行FlutterActivity的onCreate()过程,会调用FlutterMain::Init()方法
[-> flutter/shell/platform/android/flutter_main.cc]
void FlutterMain::Init(...) {
...
//初始化observer的增加和删除方法
settings.task_observer_add = [](intptr_t key, fml::closure callback) {
fml::MessageLoop::GetCurrent().AddTaskObserver(key, std::move(callback));
};
settings.task_observer_remove = [](intptr_t key) {
fml::MessageLoop::GetCurrent().RemoveTaskObserver(key);
};
}
可见,settings的task_observer_add和task_observer_remove分别对应消息循环中的增加和移除TaskObserver的功能。
DartIsolate::DartIsolate(const Settings& settings, ...)
: UIDartState(std::move(task_runners),
settings.task_observer_add,
settings.task_observer_remove, ...),
...) {
}
而在DartIsolate对象初始化过程,会将settings的这两个方法赋值其成员变量add_callback_和remove_callback_。 可见,add_callback_所对应的便是MessageLoopImpl::AddTaskObserver()方法,如下所示。
5.1.4 AddTaskObserver
[-> flutter/fml/message_loop_impl.cc]
void MessageLoopImpl::AddTaskObserver(intptr_t key, fml::closure callback) {
task_observers_[key] = std::move(callback);
}
void MessageLoopImpl::RemoveTaskObserver(intptr_t key) {
task_observers_.erase(key);
}
task_observers_是一个map类型,以UIDartState实例对象为key,以FlushMicrotasksNow()方法为value。
说明:AddTaskObserver()过程便是向task_observers_中添加相应UIDartState的FlushMicrotasksNow()方法,用于在MessageLoop过程来消费Microtask。
5.2 FlushTasks
[-> flutter/fml/message_loop_impl.cc]
void MessageLoopImpl::FlushTasks(FlushType type) {
TRACE_EVENT0("fml", "MessageLoop::FlushTasks");
std::vector<fml::closure> invocations;
{
std::lock_guard<std::mutex> lock(delayed_tasks_mutex_);
if (delayed_tasks_.empty()) {
return;
}
auto now = fml::TimePoint::Now();
//遍历整个延迟任务队列,将时间已到期的任务加入invocations
while (!delayed_tasks_.empty()) {
const auto& top = delayed_tasks_.top();
if (top.target_time > now) {
break;
}
invocations.emplace_back(std::move(top.task));
delayed_tasks_.pop();
if (type == FlushType::kSingle) {
break;
}
}
//唤醒延迟队列所在线程
WakeUp(delayed_tasks_.empty() ? fml::TimePoint::Max()
: delayed_tasks_.top().target_time);
}
//开始执行真正的遍历
for (const auto& invocation : invocations) {
invocation(); //执行已到期的任务
for (const auto& observer : task_observers_) {
observer.second(); //执行微任务 [见小节5.3]
}
}
}
该方法说明:
- 遍历整个延迟任务队列,将时间已到期的任务加入invocations队列
- 依次执行所有的任务
- 遍历执行所有的微任务,通前面小节[5.1]可知,此处observer.second()代表的是FlushMicrotasksNow()方法
5.3 FlushMicrotasksNow
[-> flutter/lib/ui/ui_dart_state.cc]
void UIDartState::FlushMicrotasksNow() {
microtask_queue_.RunMicrotasks(); //[见小节5.4]
}
5.4 RunMicrotasks
[-> third_party/tonic/dart_microtask_queue.cc]
void DartMicrotaskQueue::RunMicrotasks() {
while (!queue_.empty()) {
MicrotaskQueue local;
std::swap(queue_, local);
for (const auto& callback : local) {
if (auto dart_state = callback.dart_state().lock()) {
DartState::Scope dart_scope(dart_state.get());
//执行正在的回调方法
Dart_Handle result = Dart_InvokeClosure(callback.value(), 0, nullptr);
... //异常处理
dart_state->MessageEpilogue(result);
if (!Dart_CurrentIsolate())
return;
}
}
}
}
从前面小节[3.7]可知,此处callback.value便是_startMicrotaskLoop(),接下来回调到dart层。
5.5 _startMicrotaskLoop
[-> third_party/dart/sdk/lib/async/schedule_microtask.dart]
void _startMicrotaskLoop() {
_isInCallbackLoop = true;
try {
_microtaskLoop();
} finally {
_lastPriorityCallback = null;
_isInCallbackLoop = false;
if (_nextCallback != null) {
//当前面遍历,当链表不为空,则继续调度下一个微任务 [见小节3.4]
_AsyncRun._scheduleImmediate(_startMicrotaskLoop);
}
}
}
5.6 _microtaskLoop
[-> third_party/dart/sdk/lib/async/schedule_microtask.dart]
void _microtaskLoop() {
//遍历执行所有的微任务
while (_nextCallback != null) {
_lastPriorityCallback = null;
_AsyncCallbackEntry entry = _nextCallback;
_nextCallback = entry.next;
if (_nextCallback == null) _lastCallback = null;
(entry.callback)(); //执行微任务的回调方法
}
}
此处的callback便是前面生产MicroTask过程的小节[3.1]scheduleMicrotask()方法的参数callback。
六、总结
Flutter引擎启动过程,会创建UI/GPU/IO这3个线程,并且会为每个线程依次创建MessageLoop对象,启动后处于epoll_wait等待状态。对于Flutter的消息机制跟Android原生的消息机制有很多相似之处,都有消息(或者任务)、消息队列以及Looper,有一点不同的是Android有一个Handler类,用于发送消息以及执行回调方法,相对应Flutter中有着相近功能的便是TaskRunner。
消息机制采用的是生产者-消费者模型,本文介绍了两类任务:引擎层的Task和Dart层的Microtask,当然还有Future和DartVM中的消息处理,会在下一篇文章讲解。
- Task
- MessageLoopImpl::RegisterTask():生产Task
- MessageLoopImpl::FlushTasks():消费Task
- Microtask
- scheduleMicrotask():生成Microtask
- Window::BeginFrame():消费Microtask
- MessageLoopImpl::FlushTasks():消费Microtask
在引擎中每次消费任务时调用FlushTasks()方法,遍历整个延迟任务队列delayed_tasks_,将时间已到期的任务加入invocations,紧接着会遍历执行FlushMicrotasksNow()来消费所有的微任务。
附录
本文涉及到相关源码文件
flutter/shell/common/thread_host.cc
flutter/shell/platform/android/flutter_main.cc
flutter/fml/
- thread.cc
- task_runner.cc
- message_loop.cc
- message_loop_impl.cc
- platform/android/message_loop_android.cc
- platform/linux/timerfd.cc
flutter/lib/ui/
- dart_runtime_hooks.cc
- natives.dart
- ui_dart_state.cc
third_party/dart/sdk/lib/async/schedule_microtask.dart
third_party/dart/sdk/lib/async/zone.dart
third_party/dart/runtime/lib/schedule_microtask_patch.dart
third_party/tonic/dart_microtask_queue.cc
微信公众号 Gityuan | 微博 weibo.com/gityuan | 博客 留言区交流