Fluter訊息機制之微任務實現原理

大逗大人發表於2020-06-28

Flutter中,非同步任務主要是通過Timer及微任務來實現。在Flutter之Timer原理解析一文中,講述了通過Timer來實現非同步任務的原理,那麼本文就來看非同步任務的另一種實現,微任務的使用及其實現原理。

1、微任務的使用

先來看微任務的使用,程式碼很簡單,如下。

//用法一
Future.microtask(() {
  print("microtask1");
});
//用法二
scheduleMicrotask(() {
  print("microtask2");
});
//用法三
Zone.current.scheduleMicrotask((){
  print("microtask3");
});
//用法四
Zone.root.scheduleMicrotask((){
  print("microtask4");
});
複製程式碼

以上就是微任務的所有用法。基本上都是前兩種使用方式比較多,但前面兩種用法僅是對後面兩種用法的封裝而已。下面就來看微任務的實現原理,不過在分析微任務的實現原理之前需要先了解一下UI執行緒是如何建立的。

2、UI執行緒的建立

Flutter之Engine啟動流程一文中,提過在Engine建立過程中會建立UI執行緒、IO執行緒及GPU執行緒,但未深入。所以這裡就以Android平臺為例來深入的來了解Flutter中UI執行緒是如何建立的(IO執行緒、GPU執行緒與UI執行緒都是同一型別的物件,但命名不同)。

[-> flutter/shell/platform/android/android_shell_holder.cc]

AndroidShellHolder::AndroidShellHolder(
    flutter::Settings settings,
    fml::jni::JavaObjectWeakGlobalRef java_object,
    bool is_background_view)
    : settings_(std::move(settings)), java_object_(java_object) {
  static size_t shell_count = 1;
  auto thread_label = std::to_string(shell_count++);
            
  //建立目標執行緒
  if (is_background_view) {
    //僅建立UI執行緒
    thread_host_ = {thread_label, ThreadHost::Type::UI};
  } else {
    //建立UI執行緒、GPU執行緒及IO執行緒
    thread_host_ = {thread_label, ThreadHost::Type::UI | ThreadHost::Type::GPU |
                                      ThreadHost::Type::IO};
  }
  ...
}
複製程式碼

這裡的thread_host_是一個結構體,所以直接來看該結構體中的具體實現。

[-> flutter/shell/common/thread_host.cc]

#include "flutter/shell/common/thread_host.h"

namespace flutter {

...

ThreadHost::ThreadHost(std::string name_prefix, uint64_t mask) {
  if (mask & ThreadHost::Type::Platform) {
    //Platform執行緒的建立,在Android中,由於Platform執行緒是Android中的主執行緒,所以名稱為xxxx.platform的platform_thread不會建立
    platform_thread = std::make_unique<fml::Thread>(name_prefix + ".platform");
  }

  if (mask & ThreadHost::Type::UI) {
    //ui執行緒的建立
    ui_thread = std::make_unique<fml::Thread>(name_prefix + ".ui");
  }

  if (mask & ThreadHost::Type::GPU) {
    //gpu執行緒的建立
    gpu_thread = std::make_unique<fml::Thread>(name_prefix + ".gpu");
  }

  if (mask & ThreadHost::Type::IO) {
    //io執行緒的建立
    io_thread = std::make_unique<fml::Thread>(name_prefix + ".io");
  }
}

...

} 
複製程式碼

從上面就可以看出ui執行緒、io執行緒及GPU執行緒是同一型別的物件,但命名不同。那麼再來看UI執行緒的具體實現。

[-> flutter/fml/thread.cc]

Thread::Thread(const std::string& name) : joined_(false) {
  fml::AutoResetWaitableEvent latch;
  fml::RefPtr<fml::TaskRunner> runner;
  //建立一個thread物件
  thread_ = std::make_unique<std::thread>([&latch, &runner, name]() -> void {
    //設定當前執行緒名稱,由於這裡是UI執行緒的建立,所以name是xxx.ui
    SetCurrentThreadName(name);
    //建立一個MessageLoop物件
    fml::MessageLoop::EnsureInitializedForCurrentThread();
    //獲取MessageLoop對應的loop
    auto& loop = MessageLoop::GetCurrent();
    runner = loop.GetTaskRunner();
    //喚醒
    latch.Signal();
    //執行loop
    loop.Run();
  });
  //等待
  latch.Wait();
  task_runner_ = runner;
}
複製程式碼

Thread的建構函式中,會建立一個新執行緒。在該執行緒建立成功後,會給執行緒設定名稱,如xxxxx.ui、xxxxx.gpu、xxxxx.io等。還會給該執行緒設定一個MessageLoop物件,最後再來執行MessageLooprun函式,即使MessageLoop跑起來。

這裡重點來看MessageLoop物件的建立,它的實現如下。

[-> flutter/fml/message_loop.cc]

//tls_message_loop類似Java中的ThreadLocal,用來保證MessageLoop僅屬於某個執行緒,其他執行緒不可訪問該MessageLoop
FML_THREAD_LOCAL ThreadLocalUniquePtr<MessageLoop> tls_message_loop;

void MessageLoop::EnsureInitializedForCurrentThread() {
  //保證每個進行僅有一個MessageLoop物件
  if (tls_message_loop.get() != nullptr) {
    // Already initialized.
    return;
  }
  tls_message_loop.reset(new MessageLoop());
}

//建立MessageLoop物件
MessageLoop::MessageLoop()
      //MessageLoopImpl物件的建立
    : loop_(MessageLoopImpl::Create()),
      //TaskRunner物件的建立
      task_runner_(fml::MakeRefCounted<fml::TaskRunner>(loop_)) {}
複製程式碼

這裡重點在loop_。它是一個MessageLoopImpl物件,由於各個平臺不同,所以MessageLoopImpl的具體實現也不一樣。這裡以Android為例,當呼叫create方法時會建立一個繼承自MessageLoopImplMessageLoopAndroid物件。

[-> flutter/fml/platform/android/message_loop_android.cc]

static constexpr int kClockType = CLOCK_MONOTONIC;

static ALooper* AcquireLooperForThread() {
  ALooper* looper = ALooper_forThread();

  if (looper == nullptr) {
    //如果當前執行緒不存在looper,則建立一個新的looper
    looper = ALooper_prepare(0);
  }

  //如果當前執行緒存在looper,則獲取其引用並返回
  ALooper_acquire(looper);
  return looper;
}

//建構函式
MessageLoopAndroid::MessageLoopAndroid()
      //建立一個looper物件
    : looper_(AcquireLooperForThread()),
      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;  // continue receiving callbacks
  };

  int add_result = ::ALooper_addFd(looper_.get(),          // looper
                                   timer_fd_.get(),        // fd
                                   ALOOPER_POLL_CALLBACK,  // ident
                                   kWakeEvents,            // events
                                   read_event_fd,          // callback
                                   this                    // baton
  );
}

MessageLoopAndroid::~MessageLoopAndroid() {
  int remove_result = ::ALooper_removeFd(looper_.get(), timer_fd_.get());
  FML_CHECK(remove_result == 1);
}

//looper的執行
void MessageLoopAndroid::Run() {
  FML_DCHECK(looper_.get() == ALooper_forThread());

  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) {
      // This handles the case where the loop is terminated using ALooper APIs.
      running_ = false;
    }
  }
}

//終止事件執行
void MessageLoopAndroid::Terminate() {
  running_ = false;
  ALooper_wake(looper_.get());
}

//喚醒事件的執行
void MessageLoopAndroid::WakeUp(fml::TimePoint time_point) {
  bool result = TimerRearm(timer_fd_.get(), time_point);
  FML_DCHECK(result);
}

//監聽Loop的回撥函式
void MessageLoopAndroid::OnEventFired() {
  if (TimerDrain(timer_fd_.get())) {
    RunExpiredTasksNow();
  }
}
複製程式碼

上面程式碼其實就是通過ALooper來實現一個非同步IO。在Android中,ALooper可以認為是一個對Looper的包裝,也就是通過ALooper來操作Looper

注意:這裡所說的ALooper來操作Looper指的是Native層中的Looper,而不是framework層的Looper

MessageLoopAndroid物件建立成功後,再呼叫該物件的run函式使UI執行緒中的任務處理跑起來。這時候UI執行緒就成功建立完畢並做了相應的初始化。

以上就是在Android平臺中UI執行緒的建立,而在其他平臺,UI執行緒的建立也與Android平臺類似,唯一的不同之處就在於非同步IO的實現。比如在iOS中,非同步IO是採用CFRunLoop來實現的。

3、微任務實現原理

再回到微任務的實現中。以scheduleMicrotask方法為例,來看其程式碼實現。

void scheduleMicrotask(void callback()) {
  _Zone currentZone = Zone.current;
  //當前Zone與_rootZone是否是同一個Zone物件。
  if (identical(_rootZone, currentZone)) {
    // No need to bind the callback. We know that the root's scheduleMicrotask
    // will be invoked in the root zone.
    _rootScheduleMicrotask(null, null, _rootZone, callback);
    return;
  }
  ...
}
複製程式碼

基本上自定義Zone都不會來自定義scheduleMicrotask方法的實現,所以自定義ZonescheduleMicrotask方法最終都是呼叫_rootScheduleMicrotask方法。下面就來看該方法的實現。

void _rootScheduleMicrotask(
    Zone self, ZoneDelegate parent, Zone zone, void f()) {
  ...
  _scheduleAsyncCallback(f);
}
複製程式碼

上面程式碼很簡單,就是呼叫_scheduleAsyncCallback方法,再來看該方法的實現。

//節點為_AsyncCallbackEntry物件的單連結串列的頭節點
_AsyncCallbackEntry _nextCallback;
//節點為_AsyncCallbackEntry物件的單連結串列的尾節點
_AsyncCallbackEntry _lastCallback;

 //優先順序回撥方法放在連結串列的頭部,如果存在多個,則按照新增順序排列
_AsyncCallbackEntry _lastPriorityCallback;

//當前是否在執行回撥方法
bool _isInCallbackLoop = false;

//遍歷連結串列並執行相應的回撥方法
void _microtaskLoop() {
  while (_nextCallback != null) {
    _lastPriorityCallback = null;
    _AsyncCallbackEntry entry = _nextCallback;
    _nextCallback = entry.next;
    if (_nextCallback == null) _lastCallback = null;
    (entry.callback)();
  }
}

//開始執行回撥方法
void _startMicrotaskLoop() {
  _isInCallbackLoop = true;
  try {
    // Moved to separate function because try-finally prevents
    // good optimization.
    _microtaskLoop();
  } finally {
    _lastPriorityCallback = null;
    _isInCallbackLoop = false;
    if (_nextCallback != null) {
      _AsyncRun._scheduleImmediate(_startMicrotaskLoop);
    }
  }
}

//將回撥方法新增到連結串列中
void _scheduleAsyncCallback(_AsyncCallback callback) {
  _AsyncCallbackEntry newEntry = new _AsyncCallbackEntry(callback);
  if (_nextCallback == null) {
    _nextCallback = _lastCallback = newEntry;
    //如果當前還未處理回撥方法
    if (!_isInCallbackLoop) {
      _AsyncRun._scheduleImmediate(_startMicrotaskLoop);
    }
  } else {
    _lastCallback.next = newEntry;
    _lastCallback = newEntry;
  }
}

void _schedulePriorityAsyncCallback(_AsyncCallback callback) {
  if (_nextCallback == null) {
    _scheduleAsyncCallback(callback);
    _lastPriorityCallback = _lastCallback;
    return;
  }
  _AsyncCallbackEntry entry = new _AsyncCallbackEntry(callback);
  if (_lastPriorityCallback == null) {
    entry.next = _nextCallback;
    _nextCallback = _lastPriorityCallback = entry;
  } else {
    entry.next = _lastPriorityCallback.next;
    _lastPriorityCallback.next = entry;
    _lastPriorityCallback = entry;
    if (entry.next == null) {
      _lastCallback = entry;
    }
  }
}
複製程式碼

上面程式碼很簡單,在_scheduleAsyncCallback方法中,就是將要執行的微任務包裝成一個_AsyncCallbackEntry物件,並將該物件新增到連結串列中,也就是所有微任務都是連結串列中的一個節點,當遍歷該連結串列並執行節點中的回撥方法即是微任務的執行。

這裡要注意一下_schedulePriorityAsyncCallback方法,它也是將微任務包裝成一個_AsyncCallbackEntry物件並新增到連結串列中。但這裡的微任務優先順序高,是直接將微任務新增到連結串列的頭部。目前僅有當前Zone物件的handleUncaughtError方法中才會呼叫_schedulePriorityAsyncCallback,也就是捕獲錯誤的優先順序比普通微任務的優先順序都要高。

連結串列建立成功後,就需要能夠在合適的時機來遍歷該連結串列,這時候就來看_scheduleImmediate方法的執行。

class _AsyncRun {
  //這裡的callback對應的就是_startMicrotaskLoop方法
  external static void _scheduleImmediate(void callback());
}
複製程式碼

3.1、微任務集合

來看_scheduleImmediate方法的實現,實現程式碼很簡單,如下。

@patch
class _AsyncRun {
  @patch
  static void _scheduleImmediate(void callback()) {
    if (_ScheduleImmediate._closure == null) {
      throw new UnsupportedError("Microtasks are not supported");
    }
    _ScheduleImmediate._closure(callback);
  }
}

typedef void _ScheduleImmediateClosure(void callback());

class _ScheduleImmediate {
  static _ScheduleImmediateClosure _closure;
}

//在Engine初始化時呼叫
@pragma("vm:entry-point", "call")
void _setScheduleImmediateClosure(_ScheduleImmediateClosure closure) {
  _ScheduleImmediate._closure = closure;
}

@pragma("vm:entry-point", "call")
void _ensureScheduleImmediate() {
  _AsyncRun._scheduleImmediate(_startMicrotaskLoop);
}
複製程式碼

由於_setScheduleImmediateClosure是在RootIsolate建立成功後的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 isolate_lib = Dart_LookupLibrary(ToDart("dart:isolate"));
    Dart_Handle method_name =
        Dart_NewStringFromCString("_getIsolateScheduleImmediateClosure");
    schedule_microtask = Dart_Invoke(isolate_lib, method_name, 0, NULL);
  }
  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);
}
複製程式碼

程式碼很簡單,根據不同isolate有不同實現。先來看ui_isolate,也就是RootIsolate,在RootIsolate中,賦給_ScheduleImmediate._closure的值是_getScheduleMicrotaskClosure方法,所以來看該方法的實現。

void _scheduleMicrotask(void callback()) native 'ScheduleMicrotask';

@pragma('vm:entry-point')
Function _getScheduleMicrotaskClosure() => _scheduleMicrotask; 
複製程式碼

上面程式碼很簡單,就是對應著ScheduleMicrotask函式。

[-> flutter/lib/ui/dart_runtime_hooks.cc]

void ScheduleMicrotask(Dart_NativeArguments args) {
  Dart_Handle closure = Dart_GetNativeArgument(args, 0);
  UIDartState::Current()->ScheduleMicrotask(closure);
}
複製程式碼

ScheduleMicrotask函式中解析傳遞的引數,然後在呼叫當前UIDartState物件的ScheduleMicrotask函式。

[-> flutter/lib/ui/ui_dart_state.cc]

void UIDartState::ScheduleMicrotask(Dart_Handle closure) {
  if (tonic::LogIfError(closure) || !Dart_IsClosure(closure)) {
    return;
  }

  microtask_queue_.ScheduleMicrotask(closure);
}
複製程式碼

UIDartState物件的ScheduleMicrotask函式中,又會呼叫DartMicrotaskQueue物件的ScheduleMicrotask函式。

[-> tonic/dart_microtask_queue.cc]

void DartMicrotaskQueue::ScheduleMicrotask(Dart_Handle callback) {
  queue_.emplace_back(DartState::Current(), callback);
}
複製程式碼

最終來到DartMicrotaskQueue物件,在該物件中存在一個集合queue_,而ScheduleMicrotask函式中就是把callback新增到該集合中。也就是在RootIsolate中,最終是把_startMicrotaskLoop方法作為引數新增到集合queue_中。

再來看非ui_isolate中的處理情況,在非ui_isolate中,賦給_ScheduleImmediate._closure的值就變成了_getIsolateScheduleImmediateClosure方法。該方法的實現就簡單多了,來看下面程式碼。

_ImmediateCallback _pendingImmediateCallback;

void _isolateScheduleImmediate(void callback()) {
  _pendingImmediateCallback = callback;
}

@pragma("vm:entry-point", "call")
Function _getIsolateScheduleImmediateClosure() {
  return _isolateScheduleImmediate;
}
複製程式碼

在上面程式碼中,僅是把賦給了_pendingImmediateCallback,也就是把_startMicrotaskLoop方法作為值賦給了_pendingImmediateCallback

3.2、微任務的執行

經過前面的準備,下面就可以在合適的時機來執行_startMicrotaskLoop方法,從而來處理所有微任務。

這裡也分為ui_isolate及非ui_isolate兩種情況,先來看當前isolateui_isolate的情況。

再來看UIDartState物件的實現,除了將_startMicrotaskLoop新增到集合中,也會在該物件中通過FlushMicrotasksNow函式來執行_startMicrotaskLoop方法,程式碼如下。

[-> flutter/lib/ui/ui_dart_state.cc]

void UIDartState::FlushMicrotasksNow() {
  microtask_queue_.RunMicrotasks();
}
複製程式碼

再來看RunMicrotasks的實現,程式碼如下。

[-> 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());
        //呼叫_startMicrotaskLoop方法,callback.value()對應是_startMicrotaskLoop
        Dart_Handle result = Dart_InvokeClosure(callback.value(), 0, nullptr);
        ...
      }
    }
  }
}
複製程式碼

至此,知道了在ui_isolate中,微任務是如何新增到集合中、如何執行的。那麼再來想一個問題,微任務的執行時機是在什麼時候尼?這就需要來看呼叫FlushMicrotasksNow函式的時機。

經過檢視Flutter原始碼。可以發現,Flutter中僅在Window物件的BeginFrame函式及UIDartState物件的AddOrRemoveTaskObserver函式中呼叫了呼叫了FlushMicrotasksNow函式。因此先來看BeginFrame的實現。

[-> flutter/lib/ui/window/window.cc]

void Window::BeginFrame(fml::TimePoint frameTime) {
  std::shared_ptr<tonic::DartState> dart_state = library_.dart_state().lock();
  if (!dart_state)
    return;
  tonic::DartState::Scope scope(dart_state);

  int64_t microseconds = (frameTime - fml::TimePoint()).ToMicroseconds();
  //呼叫_beginFrame方法來開始繪製
  tonic::LogIfError(tonic::DartInvokeField(library_.value(), "_beginFrame",
                                           {
                                               Dart_NewInteger(microseconds),
                                           }));
  //執行所有微任務
  UIDartState::Current()->FlushMicrotasksNow();
  //呼叫_drawFrame來繪製UI
  tonic::LogIfError(tonic::DartInvokeField(library_.value(), "_drawFrame", {}));
}
複製程式碼

程式碼很簡單,但也說明了,在Flutter中的window呼叫_beginFrame_drawFrame方法之間會把所有微任務處理掉。也就註定了不能在微任務中做耗時操作,否則影響UI的繪製。

再來看AddOrRemoveTaskObserver函式。

[-> flutter/lib/ui/ui_dart_state.cc]

UIDartState::UIDartState(
    TaskRunners task_runners,
    TaskObserverAdd add_callback,
    TaskObserverRemove remove_callback,
    fml::WeakPtr<SnapshotDelegate> snapshot_delegate,
    fml::WeakPtr<IOManager> io_manager,
    fml::RefPtr<SkiaUnrefQueue> skia_unref_queue,
    fml::WeakPtr<ImageDecoder> image_decoder,
    std::string advisory_script_uri,
    std::string advisory_script_entrypoint,
    std::string logger_prefix,
    UnhandledExceptionCallback unhandled_exception_callback,
    std::shared_ptr<IsolateNameServer> isolate_name_server)
    : task_runners_(std::move(task_runners)),
      //給add_callback_賦值
      add_callback_(std::move(add_callback)),
      ...
      isolate_name_server_(std::move(isolate_name_server)) {
  AddOrRemoveTaskObserver(true /* add */);
}

void UIDartState::AddOrRemoveTaskObserver(bool add) {
  auto task_runner = task_runners_.GetUITaskRunner();
  if (!task_runner) {
    // This may happen in case the isolate has no thread affinity (for example,
    // the service isolate).
    return;
  }
  FML_DCHECK(add_callback_ && remove_callback_);
  if (add) {
    //這裡是一個lambda表示式,傳遞給add_callback_一個函式
    add_callback_(reinterpret_cast<intptr_t>(this),
                  [this]() { this->FlushMicrotasksNow(); });//執行所有微任務
  } else {
    remove_callback_(reinterpret_cast<intptr_t>(this));
  }
}
複製程式碼

這裡重點來看add_callback_,它是在UIDartState物件初始化的時候賦值的。由於DartIsolate繼承自UIDartState,所以來看DartIsolate物件的建立。

[-> flutter/runtime/dart_isolate.cc]

DartIsolate::DartIsolate(const Settings& settings,
                         TaskRunners task_runners,
                         fml::WeakPtr<SnapshotDelegate> snapshot_delegate,
                         fml::WeakPtr<IOManager> io_manager,
                         fml::RefPtr<SkiaUnrefQueue> unref_queue,
                         fml::WeakPtr<ImageDecoder> image_decoder,
                         std::string advisory_script_uri,
                         std::string advisory_script_entrypoint,
                         bool is_root_isolate)
    : UIDartState(std::move(task_runners),
                  //add_callback_對應的值
                  settings.task_observer_add,
                  settings.task_observer_remove,
                  std::move(snapshot_delegate),
                  std::move(io_manager),
                  std::move(unref_queue),
                  std::move(image_decoder),
                  advisory_script_uri,
                  advisory_script_entrypoint,
                  settings.log_tag,
                  settings.unhandled_exception_callback,
                  DartVMRef::GetIsolateNameServer()),
      is_root_isolate_(is_root_isolate) {
  phase_ = Phase::Uninitialized;
}
複製程式碼

在上面程式碼中,把settings物件的task_observer_add賦給了add_callback_。而settings是在FlutterMainInit函式中建立並初始化的,所以在FlutterMain初始化時,就會給settings物件的task_observer_add賦值。

[-> flutter/shell/platform/android/flutter_main.cc]

void FlutterMain::Init(JNIEnv* env,
                       jclass clazz,
                       jobject context,
                       jobjectArray jargs,
                       jstring kernelPath,
                       jstring appStoragePath,
                       jstring engineCachesPath) {
  std::vector<std::string> args;
  args.push_back("flutter");
  for (auto& arg : fml::jni::StringArrayToVector(env, jargs)) {
    args.push_back(std::move(arg));
  }
  auto command_line = fml::CommandLineFromIterators(args.begin(), args.end());

  auto settings = SettingsFromCommandLine(command_line);

  ...
  
  //add_callback_對應的函式
  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);
  };
  ...
}
複製程式碼

再來看執行緒所對應的MessageLoopImpl物件,前面說過MessageLoopAndroid繼承自MessageLoopImpl。所以來看AddTaskObserver函式的實現。

[-> flutter/fml/message_loop_impl.cc]

void MessageLoopImpl::AddTaskObserver(intptr_t key,
                                      const fml::closure& callback) {
  if (callback != nullptr) {
    //每個MessageLoopImpl物件擁有唯一的queue_id_
    task_queue_->AddTaskObserver(queue_id_, key, callback);
  } else {...}
}
複製程式碼

這裡的task_queue_是一個MessageLoopTaskQueues物件,它的AddTaskObserver函式實現如下。

[-> flutter/fml/message_loop_task_queues.cc]

void MessageLoopTaskQueues::AddTaskObserver(TaskQueueId queue_id,
                                            intptr_t key,
                                            const fml::closure& callback) {
  std::scoped_lock queue_lock(GetMutex(queue_id));
  //UIDartState為key。包含FlushMicrotasksNow函式呼叫的callback為value
  queue_entries_[queue_id]->task_observers[key] = std::move(callback);
}
複製程式碼

程式碼中的queue_entries_是一個以TaskQueueId為key、TaskQueueEntry物件為value的map,而TaskQueueEntry中的task_observers也是一個map。所以AddTaskObserver函式就是把包含FlushMicrotasksNow函式呼叫的callbackUIDartState物件為key存入map中。

Flutter之Timer原理解析一文中,如果在ui_isolate中,最終是通過DartMessageHandlerOnMessage函式來處理event handler訊息及普通訊息,程式碼如下。

[->third_party/tonic/dart_message_handler.cc]

void DartMessageHandler::Initialize(TaskDispatcher dispatcher) {
  // Only can be called once.
  TONIC_CHECK(!task_dispatcher_ && dispatcher);
  task_dispatcher_ = dispatcher;
  Dart_SetMessageNotifyCallback(MessageNotifyCallback);
}

void DartMessageHandler::OnMessage(DartState* dart_state) {
  auto task_dispatcher_ = dart_state->message_handler().task_dispatcher_;

  auto weak_dart_state = dart_state->GetWeakPtr();
  //在Android中,任務交給UI執行緒中的loop來執行。
  //在iOS中,也是通過類似loop的訊息處理器來執行
  task_dispatcher_([weak_dart_state]() {
    if (auto dart_state = weak_dart_state.lock()) {
      dart_state->message_handler().OnHandleMessage(dart_state.get());
    }
  });
}
複製程式碼

程式碼中的task_dispatcher_是在DartMessageHandler物件呼叫Initialize函式時設定的。根據Flutter之Engine啟動流程,知道是在Initialize函式是在RootIsolate初始化時呼叫的,那麼就來看一下Initialize函式的實現。

[-> flutter/runtime/dart_isolate.cc]

bool DartIsolate::InitializeIsolate(
    std::shared_ptr<DartIsolate> embedder_isolate,
    Dart_Isolate isolate,
    char** error) {
  ...
  //設定UI執行緒的訊息處理器
  SetMessageHandlingTaskRunner(GetTaskRunners().GetUITaskRunner());
  ...
  return true;
}

void DartIsolate::SetMessageHandlingTaskRunner(
    fml::RefPtr<fml::TaskRunner> runner) {
  if (!IsRootIsolate() || !runner) {
    return;
  }

  message_handling_task_runner_ = runner;
  
  //設定訊息處理器
  message_handler().Initialize(
      [runner](std::function<void()> task) { runner->PostTask(task); });
}
複製程式碼

根據上面程式碼,可以知道task_dispatcher_中其實就是將任務task通過PostTask函式新增到looper中。

[-> flutter/fml/task_runner.cc]

TaskRunner::TaskRunner(fml::RefPtr<MessageLoopImpl> loop)
    : loop_(std::move(loop)) {}
void TaskRunner::PostTask(const fml::closure& task) {
  loop_->PostTask(task, fml::TimePoint::Now());
}
複製程式碼

以Android平臺為例,這裡的loop_就是一個MessageLoopAndroid物件。所以再來看MessageLoopAndroidPostTask的實現。

[-> flutter/fml/message_loop_impl.cc]


void MessageLoopImpl::PostTask(const fml::closure& task,
                               fml::TimePoint target_time) {
  ...
  task_queue_->RegisterTask(queue_id_, task, target_time);
}
複製程式碼

PostTask函式中最終還是呼叫的task_queue_RegisterTask函式,再來看該函式的實現。

[-> flutter/fml/message_loop_task_queues.cc]

void MessageLoopTaskQueues::RegisterTask(TaskQueueId queue_id,
                                         const fml::closure& task,
                                         fml::TimePoint target_time) {
  std::scoped_lock queue_lock(GetMutex(queue_id));

  size_t order = order_++;
  const auto& queue_entry = queue_entries_[queue_id];
  queue_entry->delayed_tasks.push({order, task, target_time});
  TaskQueueId loop_to_wake = queue_id;
  if (queue_entry->subsumed_by != _kUnmerged) {
    loop_to_wake = queue_entry->subsumed_by;
  }
  WakeUpUnlocked(loop_to_wake,
                 queue_entry->delayed_tasks.top().GetTargetTime());
}

void MessageLoopTaskQueues::WakeUpUnlocked(TaskQueueId queue_id,
                                           fml::TimePoint time) const {
  if (queue_entries_.at(queue_id)->wakeable) {
    queue_entries_.at(queue_id)->wakeable->WakeUp(time);
  }
}
複製程式碼

RegisterTask函式中,把任務task新增到優先順序佇列delayed_tasks中。然後再呼叫MessageLoopAndroid物件的WakeUp函式。

[-> flutter/fml/platform/android/message_loop_android.cc]

void MessageLoopAndroid::WakeUp(fml::TimePoint time_point) {
  bool result = TimerRearm(timer_fd_.get(), time_point);
  FML_DCHECK(result);
}
複製程式碼

WakeUp函式就是通過TimerRearm函式來在合適的時機喚醒looper。根據前面UI執行緒的建立過程,可得知在looper喚醒後的回撥函式read_event_fd中是執行MessageLoopAndroid物件的OnEventFired函式,而在該函式中又直接呼叫MessageLoopAndroid物件的FlushTasks函式,下面就來看FlushTasks函式的實現。

[-> flutter/fml/message_loop_impl.cc]


void MessageLoopImpl::FlushTasks(FlushType type) {
  TRACE_EVENT0("fml", "MessageLoop::FlushTasks");
  std::vector<fml::closure> invocations;

  task_queue_->GetTasksToRunNow(queue_id_, type, invocations);

  for (const auto& invocation : invocations) {
    //執行普通回撥方法
    invocation();
    std::vector<fml::closure> observers =
        task_queue_->GetObserversToNotify(queue_id_);
    for (const auto& observer : observers) {
      //observer對應著UIDartState物件的FlushMicrotasksNow函式,這裡也就是執行所有的微任務
      observer();
    }
  }
}

void MessageLoopImpl::RunExpiredTasksNow() {
  FlushTasks(FlushType::kAll);
}
複製程式碼

FlushTasks函式中,每一個已到期的event handler任務或非同步任務執行完畢後,都會執行所有的微任務。

到此,在ui_isolate中,最終在以下兩種時機來執行微任務。

  1. 在呼叫window_beginFrame_drawFrame方法之間會把所有微任務處理掉。也就註定了不能在微任務中做耗時操作,否則影響UI的繪製。
  2. 在每一個已到期的event handler任務或非同步任務執行完畢後,都會執行所有的微任務。

再來看在非ui_isolate中微任務的執行時機。也主要分為以下幾種情況。

  1. 根據Flutter之Timer原理解析一文可得知,在每一個event handler任務或非同步任務執行完畢後,都會執行所有的微任務。
  2. 如果當前是生產環境,Isolate中訊息型別是kDrainServiceExtensionsMsg且訊息優先順序是kImmediateAction,則也會執行所有微任務

4、總結

以上就是微任務的使用及使用原理。還是有一定難度的。結合Flutter之Timer原理解析一文,基本上就可以瞭解Flutter中的訊息機制,這樣在使用微任務及其他非同步任務時也能做到了然於胸。

【參考資料】

深入理解Flutter訊息機制

相關文章