flutter
有多火这废话这里就不多说了,几乎所有大厂的app
都在用,我们开始吧
#
flutter
可以分层三层,第一层是我们 dart
的代码,包括 UI
组件、动画、Gesture
等等,也就是每次我们新建 dart
文件,需要 import
的那些包里的类:
引擎层在 github
上有一个单独的仓库 flutter/engine,这里面负责页面底层渲染,native api
的调用,包括 cpu
、 gpu
的管理调度之类。
平台相关实现 层,Flutter
会针对与渲染引擎层(engine)
约定好的接口进行对应平台(iOS、Android、Windows、 Linux)
的实现,常见不同平台的 RunLoop
、Thread
以及 Surface
绘制相关的实现 差异会在该层中体现。
值得留意的是,flutter
还使用了些 third_party
,比如页面渲染是用的 Skia
,就是 Google
的一套跨平台图形库。flutter
之所以称为自绘渲染引擎,也正是因为 Skia
保证了不同平台上渲染的强统一性
提个问题思考:
FlutterActivity
这些Java
类是属于哪一层呢?是framework
还是 engine 亦或是platform
呢?请在评论区给你出的答案~
- 方便大家学习,这里整合了
flutter
的知识点,flutter_interviews 持续更新中~
、
#
这里已 android
为例,iOS
也是类似的一通百通。flutter
可以分为两种情况,一种是纯 flutter
项目 new flutter project
另一种是已 module
的形式集成到现有项目里
- 纯
flutter
项目:编译前会调用flutter create .
在项目下init
一个android
目录,这里包含了Manifest
、build.gradle
、MainActivity
(继承了FlutterActivity
) ,完全是一个android
的项目,所以打包的app
运行时,入口类就会是/android
目录下的mainActivity
- 已
module
形式继承的话,需要我们自定义一个Activity
并让他继承自FlutterActivity
作为flutter
的入口,在这个路径里制定一个engine
,这个engine
所加载的page
会通过setContent
设置到这个Activity
上
#
不论是纯 flutter app
,还是以 module
形式继承,我们显示 Flutter
页面的 Activity
都继承自 FlutterActivity
所以当 Activity
启动时,就会调到 FlutterActivity
里的 onCreate
方法中去,那我们看看这里面做了什么
@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 主要做了各种初始化操作,代码逻辑如下
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 列表里
#
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。
@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 是一个独立的 Flutter 运行环境容器,通过它可以在 Android 应用程序中运行 Dart 代码。FlutterEngine 中的 Dart 代码可以在后台执行,也可以使用附带的 FlutterRenderer 和 Dart 代码将 Dart 端 UI 效果渲染到屏幕上,渲染可以开始和停止,从而允许 FlutterEngine 从 UI 交互转移到仅进行数据处理,然后又返回到 UI 交互的能力。
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 的作用就是架起 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的本地方法。
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++ 层实现
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
#
在AndroidShellHolder中保存有Flutter设置参数、FlutterJNI的Java引用、PlatformViewAndroid对象(该对象在后续创建)、Shell对象等。
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是Flutter应用的“中枢神经系统”,包含了多个组件,并继承它们相应的Delegate类。
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个不同的函数调用组成,用来绘制从简单的图形比特到复杂的三维景象。)
#
- 为了能让一些现有的 native 控件直接引用到 Flutter app 中,Flutter 团队提供了 AndroidView 、UIKitView 两个 widget 来满足需求
- platform view 就是 AndroidView 和 UIKitView 的总称,允许将 native view 嵌入到了 flutter widget 体系中,完成 Datr 代码对 native view 的控制
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的作用
#
我们知道 gpu线程的主要工作是将layer tree进行光栅化再发送给GPU,其中最为核心方法ScopedFrame::Raster(),这里就涉及到了 Rasterizer (光栅)
Rasterizer持有一个当前活动的在屏幕中显示的绘制Surface。Rasterizer在这个Surface上绘制从Engine中提交的layer tree
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、GrContext、SkiaUnrefQueue 都在 io 线程中创建
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_app 时,这里会生成一个 kotlin 类(已经是 Java 看来 Google 对推 kt 态度很强硬)
class MainActivity: FlutterActivity() {
}
这里默认没有实现,所以会直接调到 FlutterActivity 中,我们的启动流程主要在 onStart 里开始
@Override
protected void onStart() {
super.onStart();
lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_START);
delegate.onStart();
}
可以看到,这里通过 lifecycle 首先将生命周期设置为 ON_START ,然后 就调用了 FlutterActivityAndFragmentDelegate 的 onStart方法
#
这里主要有两个方法,ensureAlive 之前讲过了,这里主要看看 doInitialFlutterViewRun
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在FlutterEngine创建的时候创建出来,并在dartExecutor.onAttachedToJNI方法中,将DartMessager设置到FlutterJNI中,这里我们重点看看 executeDartEntrypoint 方法
public void executeDartEntrypoint(@NonNull DartEntrypoint dartEntrypoint) {
...
flutterJNI.runBundleAndSnapshotFromLibrary(
dartEntrypoint.pathToBundle, dartEntrypoint.dartEntrypointFunctionName, null, assetManager);
...
}
- 我们发现 DartExecutor 又继续调用FlutterJNI的runBundleAndSnapshotFromLibrary方法
#
我们来看看 runBundleAndSnapshotFromLibrary 方法
@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 方法
private native void nativeRunBundleAndSnapshotFromLibrary(
long nativeShellHolderId,
@NonNull String bundlePath,
@Nullable String entrypointFunctionName,
@Nullable String pathToEntrypointFunction,
@NonNull AssetManager manager);
- 这里通过 jni 会调用到 AndroidShellHolder (cpp 类) 的 Launch 方法
#
Launch 方法会做一些列的配置,最终调用Shell的RunEngine方法
void AndroidShellHolder::Launch(std::shared_ptr<AssetManager> asset_manager,
const std::string& entrypoint,
const std::string& libraryUrl) {
...
shell_->RunEngine(std::move(config.value()));
}
由此进入最核心的 Shell 层
#
值得注意的是,我们前面看过,前文中已经提到,Engine是创建、运行都在UI线程中的。所以此处Engine执行Dart代码需要在UI线程中执行。下面我们先看看 RunEngine 方法做了什么:
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 方法中
#
我们再来看下这个 Run 方法中做了什么:
// ./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 方法
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 方法
#
- 这里就到最后一步,也就是真正执行 dart 代码的地方
[[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 方法的关键
[[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 Thread)的功能是要处理平台(android/iOS)的消息。举个简单的例子,我们 MethodChannel 的回调方法 onMethodCall 就是在这个线程上
- 不过它们之间还是有区别的,一般来说,一个Flutter应用启动的时候会创建一个Engine实例,Engine创建的时候会创建一个线程供Platform Runner使用
- 但是阻塞Platform Thread虽然不会直接导致Flutter应用的卡顿,但是也不建议在这个主Runner执行繁重的操作,因为长时间卡住Platform Thread有可能会被系统的Watchdog程序强杀
#
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的指令。在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也运行在平台对应的子线程中,主要作用是做一些预先处理的读取操作,为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 这样啦