前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【 flutter 】2w 字详细解析引擎初始化、启动流程源码

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

作者头像
圆号本昊
发布2021-12-30 17:22:44
1K0
发布2021-12-30 17:22:44
举报
文章被收录于专栏:github@hornhuanggithub@hornhuang

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 这样啦

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2021-09-13 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
    • Platform 层
      • Shell 层初始化
        • flutter 启动!
        • 四个线程?
          • Platform Task Runner
            • UI Task Runner
              • GPU Task Runner
                • IO Task Runner
                • 结语
                相关产品与服务
                容器服务
                腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档