flutter: 深入通訊-傳送端

林鹿發表於2019-06-30

環境: flutter sdk v1.5.4-hotfix.1@stable 對應 flutter engine: 52c7a1e849a170be4b2b2fe34142ca2c0a6fea1f

站在平臺端的視角對通道有一個通觀概覽的認知之後就需要深入內裡對通訊機制需要一個深入剖析了,之前已經瞭解到FlutterJNI.dispatchPlatformMessage是平臺層(java)傳送資料呼叫的最後一層,那麼繼續這個呼叫序列:

FluttereJNI.dispatchPlatformMessage
  nativeDispatchPlatformMessage(FlutterJNI.java)
    DispatchPlatformMessage(platform_view_android_jni.cc:56)
      AndroidShellHolder::GetPlatformView(platform_view_android_jni.cc:421)
      PlatformViewAndroid::DispatchPlatformMessage(platform_view_android.cc:92)
        TaskRunners::GetPlatformTaskRunner => PlatformView::task_runners_
        new PlatformMessageResponseAndroid()
        new flutter::PlatformMessage(name, message, response)
        PlatformView::DispatchPlatformMessage
          PlatformView::Delegate::OnPlatformViewDispatchPlatformMessage() => Shell::On..()
            ::GetUITaskRunner
            TaskRunner::PostTask
            ...Engine::DispatchPlatformMessage
複製程式碼

傳送訊息最終呼叫到了C++層的PlatformViewAndroid::DispatchPlatformMessage方法, 又呼叫了PlatformViewAndroid成員delegate_OnPlatformViewDispatchPlatformMessage方法, 所以我們要確定PlatformView::Delegate抽象類的實現體, 也就是要追蹤成員被建立或賦值的地方。

由建構函式可知成員PlatformView::delegate_是建立時外部傳入,而PlatformViewAndroid作為子類把它的delegate傳入,所以需要了解 PlatformViewAndroid被建立時傳入的delegate物件,

android_shell_holder.cc:63可知建立PlatformViewAndroid時傳入的的delegate物件實際為Shell,在其方法中又非同步呼叫了成員engine_的方法,即Engine::DispatchPlatformMessage方法

所以我們需要

  1. 明確PlatformViewAndroid被建立的流程
  2. 明確Engine被賦值或建立的時機

建立PlatformViewAndroid流程:

AndroidShellHolder::AndroidShellHolder()
  ThreadHost::ThreadHost
    platform_thread=
  fml::MessageLoop::EnsureInitializedForCurrentThread
  platform_runner=fml::MessageLoop::GetCurrent().GetTaskRunner()
  Shell::Create()
    DartVMRef::Create(settings)
    Shell::Create()
      TaskRunner::RunNowOrPostTask
        lamda() => Shell::CreateShellOnPlatformThread()
          Shell::CreateCallback<PlatformView>(Shell&) => on_create_platform_view
            new PlatformViewAndroid(Shell,...)
  
複製程式碼

最重要的是Shell::Create這個方法,在呼叫時傳入了一個回撥,這個回撥呼叫了Shell::CreateShellOnPlatformThread(), 繼續回撥了on_create_platform_view,它的實現體在AndroidShellHolder建構函式上下文中。

建立Shell::engine_[Engine]流程:

第二個問題剛好承接了對每一個問題的分析: 我們是在建立Shell的時候建立了PlatformViewAndroid物件 shell.cc:38可知engine_也是外部傳入

Shell::CreateShellOnPlatformThread()
  new Shell()
  on_create_platform_view  => AndroidShellHolder.on_create_platform_view
    std::make_unique<PlatformViewAndroid>()
  TaskRunner::RunNowOrPostTask
  ...lamda => engine = std::make_unique<Engine>() (shell.cc:131)
  Shell::Setup
    engine_ = std::move(engine); (shell.cc:388)
複製程式碼

Shell::CreateShellOnPlatformThread中先建立了Shell例項, 接著建立了PlatformView例項,接著又非同步執行了一個lamda,建立了Engine例項

這樣前面兩個重要物件的建立時機問題就終於明確了。

繼續我們第一階段的呼叫分析, 非同步執行了Engine::DispatchPlatformMessage

Engine::DispatchPlatformMessage
  RuntimeController::DispatchPlatformMessage
    Window::DispatchPlatformMessage
      tonic::DartInvokeField(...,"_dispatchPlatformMessage")
複製程式碼

最終由此進行到了Dart層呼叫


因為在AndroidShellHolder的建構函式中Flutter建立了Shell物件,所以同樣需要明確:

建立AndroidShellHolder流程:

FlutterActivity.onCreate (FlutterActivity.java:89)
  FlutterActivityDelegate.onCreate() (FlutterActivityDelegate.java:160)
    FlutterView.FlutterView() (FlutterView.java:139)
      new FlutterNativeView(Context) (FlutterNativeView.java:47)
        FlutterNativeView.attach(this, false) (FlutterNativeView.java:165)
          FlutterJNI.attachToNative (FlutterJNI.java:423)
            AttachJNI(platform_view_android_jni.cc:149)
              java_object(env, flutterJNI)
              std::make_unique<AndroidShellHolder>(java_object)
              AndroidShellHolder::IsValid
              reinterpret_cast<jlong>
複製程式碼

比較容易發現建立的時機正是FlutterJNI繫結到FlutterNativeView, 而FlutterJNI的成員nativePlatformViewId代表的正是C++AndroidShellHolder物件,在這個過程中重要物件如ShellEngine相繼被建立。 所以每次傳送資料(或者平臺呼叫dart方法)都是FlutterJNI物件將成員nativePlatformViewId傳入c++層,轉成AndroidShellHolder物件通過Engine最終呼叫到dart層

相關文章