【 flutter 】2w 字详细解析引擎初始化、启动流程源码

2021-12-30 17:22:44 浏览数 (1)

flutter 有多火这废话这里就不多说了,几乎所有大厂的 app 都在用,我们开始吧

前言

flutter 可以分层三层,第一层是我们 dart 的代码,包括 UI 组件、动画、Gesture 等等,也就是每次我们新建 dart 文件,需要 import 的那些包里的类:

引擎层在 github 上有一个单独的仓库 flutter/engine,这里面负责页面底层渲染,native api 的调用,包括 cpugpu 的管理调度之类。

平台相关实现 层,Flutter 会针对与渲染引擎层(engine)约定好的接口进行对应平台(iOS、Android、Windows、 Linux)的实现,常见不同平台的 RunLoopThread 以及 Surface 绘制相关的实现 差异会在该层中体现。

值得留意的是,flutter 还使用了些 third_party ,比如页面渲染是用的 Skia ,就是 Google 的一套跨平台图形库。flutter 之所以称为自绘渲染引擎,也正是因为 Skia 保证了不同平台上渲染的强统一性

提个问题思考:FlutterActivity 这些 Java 类是属于哪一层呢?是 framework 还是 engine 亦或是 platform 呢?请在评论区给你出的答案~

这里已 android 为例,iOS 也是类似的一通百通。flutter 可以分为两种情况,一种是纯 flutter 项目 new flutter project 另一种是已 module 的形式集成到现有项目里

  • flutter 项目:编译前会调用 flutter create .在项目下 init 一个 android 目录,这里包含了 Manifestbuild.gradleMainActivity(继承了 FlutterActivity) ,完全是一个 android 的项目,所以打包的 app 运行时,入口类就会是 /android 目录下的 mainActivity
  • module 形式继承的话,需要我们自定义一个 Activity 并让他继承自 FlutterActivity 作为 flutter 的入口,在这个路径里制定一个 engine ,这个 engine 所加载的 page 会通过 setContent 设置到这个 Activity

Platform 层

不论是纯 flutter app ,还是以 module 形式继承,我们显示 Flutter 页面的 Activity 都继承自 FlutterActivity 所以当 Activity 启动时,就会调到 FlutterActivity 里的 onCreate 方法中去,那我们看看这里面做了什么

代码语言:javascript复制
  @Override
  protected void onCreate(@Nullable Bundle savedInstanceState) {
    switchLaunchThemeForNormalTheme();

    super.onCreate(savedInstanceState);

    lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);

    delegate = new FlutterActivityAndFragmentDelegate(this);
    delegate.onAttach(this);
    delegate.onActivityCreated(savedInstanceState);

    configureWindowForTransparency();
    setContentView(createFlutterView());
    configureStatusBarForFullscreenFlutterExperience();
  }

我们来一一解析下:

  • FlutterActivity 继承 Activity 并实现了 FlutterActivityAndFragmentDelegate 这个代理类中的Host接口及获取生命周期的LifecycleOwner接口,由此就可以对生命周期进行监听
  • configureWindowForTransparency:如果 FlutterActivity 背景模式是透明(默认是不透明的模式),则设置整个 FlutterActivity 窗口透明及隐藏状态栏。其实如果我们不在原生项目中使用FlutterModule进行混合开发的话,是不需要关注这个方法的。因为默认就是非透明模式。
  • super.onCreate:调用父类 Activity 的 onCreate 方法,进行活动的默认配置
  • FlutterActivityAndFragmentDelegate:flutter 将 FlutterActivity 和 FlutterFragment 的相同功能封装在了这个类里,也就是说这个类里包含了 flutter 运行的主要功能
  • onAttach:flutter 系统和引擎的初始化入口
  • onActivityCreated:开始执行 dart 代码,将引擎绑定到 activity/fragment
  • configureWindowForTransparency:如果 manifest 里配置为 translucent ,那么将 background 设置为透明
  • createFlutterView:创建flutterView(flutterview 将持有一个 flutterSurfaceView 或者 flutterTextureView)
  • setContentView:将 flutterview 设置为当前 activity 的 view
  • configureStatusBarForFullscreenFlutterExperience:将状态栏设置为全屏模式
onAttach

onAttach 主要做了各种初始化操作,代码逻辑如下

代码语言:javascript复制
  void onAttach(@NonNull Context context) {
    ensureAlive();
    if (flutterEngine == null) {
      setupFlutterEngine();
    }
    platformPlugin = host.providePlatformPlugin(host.getActivity(), flutterEngine);

    if (host.shouldAttachEngineToActivity()) {
      flutterEngine
          .getActivityControlSurface()
          .attachToActivity(host.getActivity(), host.getLifecycle());
    }
    
    host.configureFlutterEngine(flutterEngine);
  }
  • ensureAlive:判断当前 activity/fragment 是否已经被释放,活动和碎片的引用是在delegate 实例化时传入
  • setupFlutterEngine:初始化 flutter 引擎,首先去内存里拿,如果没有就去 host 里拿(如果是 FlutterActivity 返回为null 如果是 FlutterFragment 则参试通过 getActivity 获得),如果还没有就新建一个
  • platformPlugin:封装了很多android 的方法,比如发出提示音、退出当前页、获得剪切板内容等等
  • attachToActivity:把 Activity 注册到 FlutterEnginePluginRegistry 上,FlutterEnginePluginRegistry 也叫插件注册表。我们dependencies 里很多的库复用了原生的方法,主要逻辑带dart 实现的同时,部分逻辑在原生android/iOS 实现,这是就需要写一个 flutterPlugin 将其和 method chennel 一起注册,而这个类相当于把这些插件进行统一的管理,attachToActivity方法的作用就是通知当前连接到FlutterEngine的所有插件,将它们附加到Activity
  • configureFlutterEngine:通过在 FlutterActivity/Fragment 里重写该方法,对 engine 做更多的操作,比如 FlutterActivity 会把这个 flutterRngine 注册到 pubspec.yaml 的 plugins 列表里
createFlutterView

FlutterView 的作用是在 Android 设备上显示一个 Flutter UI,绘制内容来自于 FlutterEngine 提供。android 中 flutterview 有四种沿伸,首先是用于显示我们app对象的 FlutterSurfaceView 或者 FlutterTextureView ,但是除了这两个之外还有 FlutterSplashView 和 FlutterImageView

  • FlutterSplashView 的主要作用是在 FlutterView render 渲染出来之前显示一个 SplashScreen(本质 Drawable)过渡图(可以理解成类似开屏图片)。
  • FlutterView 创建时依赖一个 FlutterTextureView 或者 FlutterSurfaceView,其判断条件的本质就是看 FlutterActivity 的 window 窗体背景是否透明(FlutterFragment 时通过 Arguments 的 flutterview_render_mode 参数来决定),不透明就是 surface,透明就是 texture。
代码语言:javascript复制
  @NonNull
  View onCreateView(
	...
    if (host.getRenderMode() == RenderMode.surface) {
      ...
      flutterView = new FlutterView(host.getActivity(), flutterSurfaceView);
    } else {
      ...
      flutterView = new FlutterView(host.getActivity(), flutterTextureView);
    }

    flutterView.addOnFirstFrameRenderedListener(flutterUiDisplayListener);

    flutterSplashView = new FlutterSplashView(host.getContext());
    ...
    flutterView.attachToFlutterEngine(flutterEngine);

    return flutterSplashView;
  }
  • attachToFlutterEngine:这个方法里将FlutterSurfaceView的Surface提供给指定的FlutterRender,用于将Flutter UI绘制到当前的FlutterSurfaceView
FlutterEngine

FlutterEngine 是一个独立的 Flutter 运行环境容器,通过它可以在 Android 应用程序中运行 Dart 代码。FlutterEngine 中的 Dart 代码可以在后台执行,也可以使用附带的 FlutterRenderer 和 Dart 代码将 Dart 端 UI 效果渲染到屏幕上,渲染可以开始和停止,从而允许 FlutterEngine 从 UI 交互转移到仅进行数据处理,然后又返回到 UI 交互的能力。

代码语言:javascript复制
public FlutterEngine(
    @NonNull Context context,
    @NonNull FlutterLoader flutterLoader,
    @NonNull FlutterJNI flutterJNI,
    @NonNull PlatformViewsController platformViewsController,
    @Nullable String[] dartVmArgs,
    boolean automaticallyRegisterPlugins) {
  this.flutterJNI = flutterJNI;
  flutterLoader.startInitialization(context.getApplicationContext());
  ...
  attachToJni();
  this.dartExecutor = new DartExecutor(flutterJNI, context.getAssets());
  this.dartExecutor.onAttachedToJNI();
  this.renderer = new FlutterRenderer(flutterJNI);
  ...
  xxxChannel = new XxxChannel(...); // 创建各个消息通道,用于传递事件、消息
  ...
}
  • FlutterJNI:engine 层的 JNI 接口都在这里面注册、绑定。通过他可以调用 engine 层的 c/c 代码
  • DartExecutor:用于执行 Dart 代码(调用 DartExecutor 的executeDartEntrypoint(DartExecutor.DartEntrypoint)即可执行,一个 FlutterEngine 执行一次)
  • FlutterRenderer:FlutterRenderer 实例 attach 上一个 RenderSurface 也就是之前说的 FlutterView

这里 FlutterEngine 与 DartExecutor、Dart VM、Isolate 的关系大致可以归纳如下:

  • 一个Native进程只有一个DartVM。
  • 一个DartVM(或说一个Native进程)可以有多个FlutterEngine。
  • 多个FlutterEngine运行在各自的Isolate中,他们的内存数据不共享,需要通过Isolate事先设置的port(顶级函数)通讯。
  • FlutterEngine可以后台运行代码,不渲染UI;也可以通过FlutterRender渲染UI。
  • 初始化第一个FlutterEngine时,DartVM会被创建,之后不会再有其他DartVM环境被创建。
  • FlutterEngine可以通过FlutterEngineCache管理缓存,建议使用阿里闲鱼的flutter_boost来管理Native&Flutter页面混合的项目。
  • 我们可以手动改动Flutter项目的入口函数、flutter_assets资源路径、flutter项目初始Route等参数。涉及到的API有FlutterLoader
  • DartExecutor、FlutterJNI、Host等等。简单描述下,就是使用BinaryMessager传输数据,在修改入口函数、初始化Route参数之后在调用DartExecutor的执行代码
FlutterJNI

FlutterJNI 的作用就是架起 Android 端 Java 与 Flutter Engine C/C 端的一座接口桥梁。为了方便 JNI 接口的管理,这里将所有的 JNI 接口都封装在了 FlutterJNI 里,方便使用 大部分FlutterJNI中的调用都与特定的“platform view”相关,而“platform view”的数量可能会很多。所以,在执行了attachToNative方法后,每个FlutterJNI实例都持有一个本地“platform view”的ID,且这个ID与bendingC/C 引擎代码共享。这个ID会被传递到所有的具体platform view的本地方法。

代码语言:javascript复制
public class FlutterJNI {
  ...
  public FlutterJNI() {
    // We cache the main looper so that we can ensure calls are made on the main thread
    // without consistently paying the synchronization cost of getMainLooper().
    mainLooper = Looper.getMainLooper();
  }
  ...
  @UiThread
  public void attachToNative(boolean isBackgroundView) {
    ensureRunningOnMainThread();
    ensureNotAttachedToNative();
    nativePlatformViewId = nativeAttach(this, isBackgroundView);
  }

  private native long nativeAttach(@NonNull FlutterJNI flutterJNI, boolean isBackgroundView);
  ...
}

在 platform 层也会针对 android/ios 实现对应的 flutter_jni 类,那 android 举例就是 platform_view_android_jni.cc,这里面注册了 FlutterJNI 中调用的 c/c 层实现

代码语言:javascript复制
bool RegisterApi(JNIEnv* env) {
  static const JNINativeMethod flutter_jni_methods[] = {
      {
          .name = "nativeAttach",
          .signature = "(Lio/flutter/embedding/engine/FlutterJNI;Z)J",
          .fnPtr = reinterpret_cast<void*>(&AttachJNI),
      },
      ...
  };
  if (env->RegisterNatives(g_flutter_jni_class->obj(), flutter_jni_methods,
                           fml::size(flutter_jni_methods)) != 0) {
    FML_LOG(ERROR) << "Failed to RegisterNatives with FlutterJNI";
    return false;
  }
  ...
}

static jlong AttachJNI(JNIEnv* env,
                       jclass clazz,
                       jobject flutterJNI,
                       jboolean is_background_view) {
  fml::jni::JavaObjectWeakGlobalRef java_object(env, flutterJNI);
  auto shell_holder = std::make_unique<AndroidShellHolder>( // [6]
      FlutterMain::Get().GetSettings(), java_object, is_background_view);
  if (shell_holder->IsValid()) {
    return reinterpret_cast<jlong>(shell_holder.release());
  } else {
    return 0;
  }
}
  • AndroidShellHolder:这是 C/C 的 Shell 持有类,Flutter的引擎采用核心技术,Skia,一个2D图形渲染库,Dart,一个用于垃圾收集的面向对象语言的VM,并将它们托管在一个shell中。不同的平台有不同的shell
FlutterJNIAndroidShellHolder

在AndroidShellHolder中保存有Flutter设置参数、FlutterJNI的Java引用、PlatformViewAndroid对象(该对象在后续创建)、Shell对象等。

代码语言:javascript复制
AndroidShellHolder::AndroidShellHolder(
    flutter::Settings settings,
    fml::jni::JavaObjectWeakGlobalRef java_object,
    bool is_background_view)
    : settings_(std::move(settings)), java_object_(java_object) {
  ...
  // 创建三个线程:UI线程、GPU线程、IO线程
  thread_host_ = {thread_label, ThreadHost::Type::UI | ThreadHost::Type::GPU |
                                    ThreadHost::Type::IO};
  ...
  fml::WeakPtr<PlatformViewAndroid> weak_platform_view;
  Shell::CreateCallback<PlatformView> on_create_platform_view =
      [is_background_view, java_object, &weak_platform_view](Shell& shell) {
        std::unique_ptr<PlatformViewAndroid> platform_view_android;
        ...
        platform_view_android = std::make_unique<PlatformViewAndroid>( // [7]
            shell,                   // delegate
            shell.GetTaskRunners(),  // task runners
            java_object,             // java object handle for JNI interop
            shell.GetSettings()
                .enable_software_rendering  // use software rendering
        ); 
        weak_platform_view = platform_view_android->GetWeakPtr();
        return platform_view_android;
      };
  ...
  // [8]
  shell_ =
      Shell::Create(task_runners,             // task runners
                    GetDefaultWindowData(),   // window data
                    settings_,                // settings
                    on_create_platform_view,  // platform view create callback
                    on_create_rasterizer      // rasterizer create callback
      );
  platform_view_ = weak_platform_view;
  ...
}
  • PlatformViewAndroid:的创建,负责管理平台侧是事件处理在UI线程执行
  • Shell:加载第三方库,Java虚拟机的创建

Shell 层初始化

Shell是Flutter应用的“中枢神经系统”,包含了多个组件,并继承它们相应的Delegate类。

代码语言:javascript复制
std::unique_ptr<Shell> Shell::Create(
    TaskRunners task_runners,
    const WindowData window_data,
    Settings settings,
    Shell::CreateCallback<PlatformView> on_create_platform_view,
    Shell::CreateCallback<Rasterizer> on_create_rasterizer) {
  ...
  auto vm = DartVMRef::Create(settings); // 创建Dart虚拟机
  auto vm_data = vm->GetVMData();
  return Shell::Create(std::move(task_runners),        //
                       std::move(window_data),         //
                       std::move(settings),            //
                       vm_data->GetIsolateSnapshot(),  // isolate snapshot
                       on_create_platform_view,        //
                       on_create_rasterizer,           //
                       std::move(vm)                   //
  );
}

在platform线程中创建了Shell,之后分别在栅格化线程中创建Rasterizer,在platform线程中创建PlatformView,在IO线程中创建ShellIOManager,在UI线程中创建Engine,并将这四者设置到Shell中去。Shell分别继承了四者的Delegate,四者通过相应的Delegate将事件传递到Shell。

  • Rasterizer:图形学 光栅化详解(Rasterization)
  • PlatformView:flutter 官方提供的一个可以嵌入 Android 和 iOS 平台原生 View 的 widget。利用viewId查找,底层是flutter 引擎进行绘制和渲染。主要适用于flutter中不太容易实现的widget(Native中已经很成熟,并且很有优势的View),如WebView、视频播放器、地图等。
  • ShellIOManager:这里面回创建 OpenGL Context (是用于渲染2D、3D矢量图形的跨语言、跨平台的应用程序编程接口(API)。 这个接口由近350个不同的函数调用组成,用来绘制从简单的图形比特到复杂的三维景象。)
PlatformView
  • 为了能让一些现有的 native 控件直接引用到 Flutter app 中,Flutter 团队提供了 AndroidView 、UIKitView 两个 widget 来满足需求
  • platform view 就是 AndroidView 和 UIKitView 的总称,允许将 native view 嵌入到了 flutter widget 体系中,完成 Datr 代码对 native view 的控制
代码语言:javascript复制
void PlatformViewAndroid::NotifyCreated(
    fml::RefPtr<AndroidNativeWindow> native_window) {
  if (android_surface_) {
    InstallFirstFrameCallback();
    ...
    fml::TaskRunner::RunNowOrPostTask(
        task_runners_.GetRasterTaskRunner(),
        [&latch, surface = android_surface_.get(),
         native_window = std::move(native_window)]() {
          surface->SetNativeWindow(native_window);
          ...
        });
    ...
  }

  PlatformView::NotifyCreated();
}


void PlatformView::NotifyCreated() {
  std::unique_ptr<Surface> surface;
  auto* platform_view = this;
  ...
  fml::TaskRunner::RunNowOrPostTask(
      task_runners_.GetRasterTaskRunner(), [platform_view, &surface, &latch]() {
        surface = platform_view->CreateRenderingSurface();
        ...
      });
  ...
  delegate_.OnPlatformViewCreated(std::move(surface));
}
  • PlatformViewAndroid 在 JNI 层进行 View 的绘制和事件处理,注册 SurfaceView 给Flutter Eingine,提供给引擎进行绘制的画布,调用ANative_window类来连接FlutterUI和AndroidUI的桥梁
  • 主要实现的功能就是将native_window设置到surface中,再将这个surface通知到delegate(也就是Shell)中。也就是说,PlatformView主要起到一个沟通Surface和Shell的作用
Rasterizer

我们知道 gpu线程的主要工作是将layer tree进行光栅化再发送给GPU,其中最为核心方法ScopedFrame::Raster(),这里就涉及到了 Rasterizer (光栅) Rasterizer持有一个当前活动的在屏幕中显示的绘制Surface。Rasterizer在这个Surface上绘制从Engine中提交的layer tree

代码语言:javascript复制
Rasterizer::Rasterizer(Delegate& delegate, TaskRunners task_runners)
    : Rasterizer(delegate,
                 std::move(task_runners),
                 std::make_unique<flutter::CompositorContext>(
                     delegate.GetFrameBudget())) {}
...
void Rasterizer::Draw(
    fml::RefPtr<flutter::Pipeline<flow::LayerTree>> pipeline) {
  TRACE_EVENT0("flutter", "GPURasterizer::Draw");

  flutter::Pipeline<flow::LayerTree>::Consumer consumer =
      std::bind(&Rasterizer::DoDraw, this, std::placeholders::_1);

  //消费pipeline的任务 [见小节2.3]
  switch (pipeline->Consume(consumer)) {
    case flutter::PipelineConsumeResult::MoreAvailable: {
      task_runners_.GetGPUTaskRunner()->PostTask(
          [weak_this = weak_factory_.GetWeakPtr(), pipeline]() {
            if (weak_this) {
              weak_this->Draw(pipeline);
            }
          });
      break;
    }
    default:
      break;
  }
}
  • 其中执行完 Consume() 后返回值为 PipelineConsumeResult::MoreAvailable,则说明还有任务需要处理,则再次执行不同 LayerTree 的 Draw() 过程
ShellIOManager

ShellIOManager、GrContext、SkiaUnrefQueue 都在 io 线程中创建

代码语言:javascript复制
class ShellIOManager final : public IOManager {
  ...
  void NotifyResourceContextAvailable(sk_sp<GrContext> resource_context);
  void UpdateResourceContext(sk_sp<GrContext> resource_context);
  ...
  fml::WeakPtr<GrContext> GetResourceContext() const override;
  fml::RefPtr<flutter::SkiaUnrefQueue> GetSkiaUnrefQueue() const override;
}
  • shellIOManager继承自IOManager类。IOManager是管理获取GrContext资源和Skia队列的方法的接口类。这两者都属于图形绘制相关内容,在后续文章中进行分析
  • NotifyResourceContextAvailable和UpdateResourceContext方法是通知GrContext创建和获取的方法

flutter 启动!

初始化完毕,万事俱备只欠东风门,我们看看 flutter 是怎么启动的!

FlutterActivity

当我们创建一个 flutter_app 时,这里会生成一个 kotlin 类(已经是 Java 看来 Google 对推 kt 态度很强硬)

代码语言:javascript复制
class MainActivity: FlutterActivity() {
}

这里默认没有实现,所以会直接调到 FlutterActivity 中,我们的启动流程主要在 onStart 里开始

代码语言:javascript复制
@Override
protected void onStart() {
  super.onStart();
  lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_START);
  delegate.onStart();
}

可以看到,这里通过 lifecycle 首先将生命周期设置为 ON_START ,然后 就调用了 FlutterActivityAndFragmentDelegate 的 onStart方法

FlutterActivityAndFragmentDelegate

这里主要有两个方法,ensureAlive 之前讲过了,这里主要看看 doInitialFlutterViewRun

代码语言:javascript复制
void onStart() {
  ensureAlive();
  doInitialFlutterViewRun();
}
...
private void doInitialFlutterViewRun() {
  ...
  if (flutterEngine.getDartExecutor().isExecutingDart()) {
    // No warning is logged because this situation will happen on every config
    // change if the developer does not choose to retain the Fragment instance.
    // So this is expected behavior in many cases.
    return;
  }
  ...
  // Configure the Dart entrypoint and execute it.
  DartExecutor.DartEntrypoint entrypoint =
      new DartExecutor.DartEntrypoint(
          host.getAppBundlePath(), host.getDartEntrypointFunctionName());
  flutterEngine.getDartExecutor().executeDartEntrypoint(entrypoint); // [9]
}

由于 FlutterView 中不支持重新加载,或者重启 dart 。所以要先判断当前是否转载执行 dart 代码

  • 接着通过 FlutterActivity 获取包路径和 Dart 入口方法,并创建出 DartEntrypoint ,后续就通过 DartEntrypoint 来执行 dart 代码
  • 然后会通过 flutterEngine 调用 Executor 的 executeDartEntrypoint 方法
DartExecutor

前面我们说过,DartExecutor在FlutterEngine创建的时候创建出来,并在dartExecutor.onAttachedToJNI方法中,将DartMessager设置到FlutterJNI中,这里我们重点看看 executeDartEntrypoint 方法

代码语言:javascript复制
public void executeDartEntrypoint(@NonNull DartEntrypoint dartEntrypoint) {
  ...
  flutterJNI.runBundleAndSnapshotFromLibrary(
      dartEntrypoint.pathToBundle, dartEntrypoint.dartEntrypointFunctionName, null, assetManager);
  ...
}
  • 我们发现 DartExecutor 又继续调用FlutterJNI的runBundleAndSnapshotFromLibrary方法
FlutterJNI

我们来看看 runBundleAndSnapshotFromLibrary 方法

代码语言:javascript复制
  @UiThread
  public void runBundleAndSnapshotFromLibrary( @NonNull String bundlePath, @Nullable String entrypointFunctionName, @Nullable String  @NonNull AssetManager assetManager) {
    ensureRunningOnMainThread();
    ensureAttachedToNative();
    nativeRunBundleAndSnapshotFromLibrary(
        nativeShellHolderId,
        bundlePath,
        entrypointFunctionName,
        pathToEntrypointFunction,
        assetManager);
  }
  • nativeRunBundleAndSnapshotFromLibrary 这是一个 native 方法
代码语言:javascript复制
  private native void nativeRunBundleAndSnapshotFromLibrary(
      long nativeShellHolderId,
      @NonNull String bundlePath,
      @Nullable String entrypointFunctionName,
      @Nullable String pathToEntrypointFunction,
      @NonNull AssetManager manager);
  • 这里通过 jni 会调用到 AndroidShellHolder (cpp 类) 的 Launch 方法
AndroidShellHolder

Launch 方法会做一些列的配置,最终调用Shell的RunEngine方法

代码语言:javascript复制
void AndroidShellHolder::Launch(std::shared_ptr<AssetManager> asset_manager,
                                const std::string& entrypoint,
                                const std::string& libraryUrl) {
  ...
  shell_->RunEngine(std::move(config.value()));
}

由此进入最核心的 Shell 层

Shell

值得注意的是,我们前面看过,前文中已经提到,Engine是创建、运行都在UI线程中的。所以此处Engine执行Dart代码需要在UI线程中执行。下面我们先看看 RunEngine 方法做了什么:

代码语言:javascript复制
void Shell::RunEngine(
    RunConfiguration run_configuration,
    const std::function<void(Engine::RunStatus)>& result_callback) {
    ...
  fml::TaskRunner::RunNowOrPostTask(
      task_runners_.GetUITaskRunner(), // [10]
      fml::MakeCopyable(
          [run_configuration = std::move(run_configuration),
           weak_engine = weak_engine_, result]() mutable {
            ...
            auto run_result = weak_engine->Run(std::move(run_configuration));
            ...
            result(run_result);
          }));
}
  • 这里又会进入到 Engine 的 Run 方法中
Engine

我们再来看下这个 Run 方法中做了什么:

代码语言:javascript复制
// ./shell/common/engine.cc
Engine::RunStatus Engine::Run(RunConfiguration configuration) {
  ...
  last_entry_point_ = configuration.GetEntrypoint();
  last_entry_point_library_ = configuration.GetEntrypointLibrary();
  auto isolate_launch_status =
      PrepareAndLaunchIsolate(std::move(configuration)); // [11]
  ...
  std::shared_ptr<DartIsolate> isolate =
      runtime_controller_->GetRootIsolate().lock();

  bool isolate_running =
      isolate && isolate->GetPhase() == DartIsolate::Phase::Running;

  if (isolate_running) {
    ...
    std::string service_id = isolate->GetServiceId();
    fml::RefPtr<PlatformMessage> service_id_message =
        fml::MakeRefCounted<flutter::PlatformMessage>(
            kIsolateChannel, // 此处设置为IsolateChannel
            std::vector<uint8_t>(service_id.begin(), service_id.end()),
            nullptr);
    HandlePlatformMessage(service_id_message); // [12]
  }
  return isolate_running ? Engine::RunStatus::Success
                         : Engine::RunStatus::Failure;
}
  • PrepareAndLaunchIsolate:这个方法用于启动 Isolate ,同时我们的 dart 代码也是在这个方法里运行
  • HandlePlatformMssage:这里面会将 DartIsolate的状态传递到Platform层进行处理
  • 这里我们要研究dart 代码如何启动,所以我们要接着看 PrepareAndLaunchIsolate 方法
代码语言:javascript复制
Engine::RunStatus Engine::PrepareAndLaunchIsolate(
    RunConfiguration configuration) {
  UpdateAssetManager(configuration.GetAssetManager());
  auto isolate_configuration = configuration.TakeIsolateConfiguration();
  std::shared_ptr<DartIsolate> isolate =
      runtime_controller_->GetRootIsolate().lock();
  ...
  if (!isolate_configuration->PrepareIsolate(*isolate)) {
    return RunStatus::Failure;
  }
  if (configuration.GetEntrypointLibrary().empty()) { // 之前传入的library为空,进入该分支
    if (!isolate->Run(configuration.GetEntrypoint(),
                      settings_.dart_entrypoint_args)) {
      return RunStatus::Failure;
    }
  } else {
    if (!isolate->RunFromLibrary(configuration.GetEntrypointLibrary(),
                                 configuration.GetEntrypoint(),
                                 settings_.dart_entrypoint_args)) {
      return RunStatus::Failure;
    }
  }
}
  • 这里经过一系列判断后,会进入 dart_isolate 的 Run 方法
DartIsolate
  • 这里就到最后一步,也就是真正执行 dart 代码的地方
代码语言:javascript复制
[[nodiscard]] bool DartIsolate::Run(const std::string& entrypoint_name,
                                    const std::vector<std::string>& args,
                                    const fml::closure& on_run) {
  if (phase_ != Phase::Ready) {
    return false;
  }

  tonic::DartState::Scope scope(this);

  auto user_entrypoint_function =
      Dart_GetField(Dart_RootLibrary(), tonic::ToDart(entrypoint_name.c_str()));

  auto entrypoint_args = tonic::ToDart(args);

  if (!InvokeMainEntrypoint(user_entrypoint_function, entrypoint_args)) {
    return false;
  }

  phase_ = Phase::Running;

  if (on_run) {
    on_run();
  }
  return true;
}
  • 这里首先将 Isolate 的状态设置为 Running 接着调用 dart 的 main 方法。这里 InvokeMainEntrypoint 是执行 main 方法的关键
代码语言:javascript复制
[[nodiscard]] static bool InvokeMainEntrypoint(
    Dart_Handle user_entrypoint_function,
    Dart_Handle args) {
  ...
  Dart_Handle start_main_isolate_function =
      tonic::DartInvokeField(Dart_LookupLibrary(tonic::ToDart("dart:isolate")),
                             "_getStartMainIsolateFunction", {});
  ...
  if (tonic::LogIfError(tonic::DartInvokeField(
          Dart_LookupLibrary(tonic::ToDart("dart:ui")), "_runMainZoned",
          {start_main_isolate_function, user_entrypoint_function, args}))) {
    return false;
  }
  return true;
}

大功告成,到这里 dart 代码就运行起来啦!

四个线程?

前面 FlutterJNIAndroidShellHolder 里我们看到,它创建出了三个线程 ui、gpu、io 在后面运行过程中,比如光栅那里,他们都被反复的使用,那么他们到底是什么又有什么作用呢? Flutter引擎本身并不创建也不管理自己的线程,线程的管控是由embedder层负责的。引擎要求Embedder提供4个task runner的引用,但是并不关心每个runner所在的线程是否是独立的。 可是我们上面只看到了三个呀?其实还有一个 platform 线程,指的是我们平台的主线程,拿 android 为例,就是我们的 main 线程,那么四个线程是做什么用呢,这里简要分享一下:

注意:每个引擎有一份独立的UI, GPU,IO Runnter独立线程;所有共享引擎共用platform runner的线程

Platform Task Runner

Platform Task Runner (或者叫 Platform Thread)的功能是要处理平台(android/iOS)的消息。举个简单的例子,我们 MethodChannel 的回调方法 onMethodCall 就是在这个线程上

  • 不过它们之间还是有区别的,一般来说,一个Flutter应用启动的时候会创建一个Engine实例,Engine创建的时候会创建一个线程供Platform Runner使用
  • 但是阻塞Platform Thread虽然不会直接导致Flutter应用的卡顿,但是也不建议在这个主Runner执行繁重的操作,因为长时间卡住Platform Thread有可能会被系统的Watchdog程序强杀

UI Task Runner

UI Task Runner用于执行Root Isolate代码,它运行在线程对应平台的线程上,属于子线程。同时,Root isolate在引擎启动时会绑定了不少Flutter需要的函数方法,以便进行渲染操作。

  • 当新的一帧到来时,引擎通过Root Isolate通知Flutter Engine有帧需要渲染,平台收到Flutter Engine通知后会创建对象和组件并生成一个Layer Tree,然后将生成的Layer Tree提交给Flutter Engine。此时,只生成了需要绘制的内容,并没有执行屏幕渲染,而Root Isolate就是负责将创建的Layer Tree绘制到屏幕上,因此如果线程过载会导致卡顿掉帧
  • 这里要注意的是:阻塞这个线程会直接导致Flutter应用卡顿掉帧。所以在实际使用过程中,建议为每一个Engine实例都新建一个专用的GPU Runner线程,或者说 创建其他的isolate,创建的isolate没有绑定Flutter的功能,只能做数据运算,不能调用Flutter的功能,而且创建的isolate的生命周期受Root isolate控制,Root isolate停止,其他的isolate也会停止,而且创建的isolate运行的线程,是DartVM里的线程池提供的

GPU Task Runner

GPU Task Runner主要用于执行设备GPU的指令。在UI Task Runner 创建layer tree,在GPU Task Runner将Layer Tree提供的信息转化为平台可执行的GPU指令。除了将Layer Tree提供的信息转化为平台可执行的GPU指令,GPU Task Runner同时也负责管理每一帧绘制所需要的GPU资源,包括平台Framebuffer的创建,Surface生命周期管理,以及Texture和Buffers的绘制时机等

  • 一般来说,UI Runner和GPU Runner运行在不同的线程。GPU Runner会根据目前帧执行的进度去向UI Runner请求下一帧的数据,在任务繁重的时候还可能会出现UI Runner的延迟任务。不过这种调度机制的好处在于,确保GPU Runner不至于过载,同时也避免了UI Runner不必要的资源消耗
  • GPU Runner可以导致UI Runner的帧调度的延迟,GPU Runner的过载会导致Flutter应用的卡顿,因此在实际使用过程中,建议为每一个Engine实例都新建一个专用的GPU Task Runner线程

IO Task Runner

IO Task Runner也运行在平台对应的子线程中,主要作用是做一些预先处理的读取操作,为GPU Runner的渲染操作做准备。我们可以认为IO Task Runner是GPU Task Runner的助手,它可以减少GPU Task Runner的额外工作。例如,在Texture的准备过程中,IO Runner首先会读取压缩的图片二进制数据,并将其解压转换成GPU能够处理的格式,然后再将数据传递给GPU进行渲染。

  • 在IO Task Runner不会阻塞Flutter,虽然在加载图片和资源的时候可能会延迟,但是还是建议为IO Task Runner单独开一个线程。

结语

答案:FlutterActivity、FlutterVC 等嵌入在歌平台原生层的业务类,都属于 platform 层。你答对了吗? 其实对于 flutter 的架构图,native 开发者需要从下往上看 platform - engine - framework,而 flutter 开发者则是从上往下看 framework - engine - platform 这样啦

0 人点赞