先从一张架构预览图说起吧:
Flutter架构分 Framework/Engine/Embedder三层:
Framework
Framework层是纯dart语言写的,它实现了一套用于处理Animation、Painting和Gestures的基础功能,并基于Painting封装了一套widget用来抹平跨平台UI展示的差异
以及提供了一套减少由于UI改变控件重复创建销毁带来的性能消耗
Embedder层
什么是Embedder ?
个人理解是flutter想要在不同的平台上运行就要在不同的平台上创建容器详情点击这里
比如在Android上就要创建FlutterActivity/FlutterFragment/FlutterView
通过查看源码可以发现Embedder层主要负责 Engine的初始化 线程设置 渲染组件的设置 资源文件的初始化
Engine层
Engine层是由C++编写是flutter的核心负责初始化,Dart虚拟机管理,Flutter底层的通信和事件机制
Embedder如何和Engine建立关系
跟踪FlutterEmbedder层代码中会发现不论是FlutterActivity还是FlutterFragment最终Flutter的容器还是FlutterView,而FlutterView只不过继承了SurfaceView
/**
* An Android view containing a Flutter app.
*/
public class FlutterView extends SurfaceView implements BinaryMessenger, TextureRegistry {
//...
if (nativeView == null) {
mNativeView = new FlutterNativeView(activity.getApplicationContext());
} else {
mNativeView = nativeView;
}
dartExecutor = mNativeView.getDartExecutor();
flutterRenderer = new FlutterRenderer(mNativeView.getFlutterJNI());
//...
mNativeView.attachViewAndActivity(this, activity);
mSurfaceCallback = new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder holder) {
assertAttached();
mNativeView.getFlutterJNI().onSurfaceCreated(holder.getSurface());
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
assertAttached();
mNativeView.getFlutterJNI().onSurfaceChanged(width, height);
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
assertAttached();
mNativeView.getFlutterJNI().onSurfaceDestroyed();
}
};
getHolder().addCallback(mSurfaceCallback);
// ...
}
surface的改变会通知nativeview,FlutterView好像就是nativeview的装饰器,那么让我们进入FlutterNativeView看看:
public FlutterNativeView(@NonNull Context context, boolean isBackgroundView) {
//...
mFlutterJNI = new FlutterJNI();
//...构造后直接调用attach方法调用和Engine层建立关系
attach(this, isBackgroundView);
}
FlutterJNI getFlutterJNI() {
return mFlutterJNI;
}
private void attach(FlutterNativeView view, boolean isBackgroundView) {
mFlutterJNI.attachToNative(isBackgroundView);
dartExecutor.onAttachedToJNI();
}
由此可以看出Navtive是通过FlutterJNI和JNI层关联并初始化Engine
关联并Engine初始化
attachToNative 的代码位置在这里:/flutter/shell/platform/android/platform_view_android_jni.cc
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(
FlutterMain::Get().GetSettings(), java_object, is_background_view);
if (shell_holder->IsValid()) {
return reinterpret_cast(shell_holder.release());
} else {
return 0;
}
}
reinterpret_cast是C++里的强制类型转换符。这里reinterpret_cast()方法将该AndroidShellHolder对象指针强制转化为long类型的值并返回java层保存。
那就从AndroidShellHolder分析:/flutter/shell/platform/android/android_shell_holder.cc
step1: 创建四个线程 并把当前线程设置platform thread
// The current thread will be used as the platform thread. Ensure that the
// message loop is initialized.
fml::MessageLoop::EnsureInitializedForCurrentThread();
fml::RefPtr gpu_runner;
fml::RefPtr ui_runner;
fml::RefPtr io_runner;
fml::RefPtr platform_runner =
fml::MessageLoop::GetCurrent().GetTaskRunner();
if (is_background_view) {
...
} else {
gpu_runner = thread_host_.gpu_thread->GetTaskRunner();
ui_runner = thread_host_.ui_thread->GetTaskRunner();
io_runner = thread_host_.io_thread->GetTaskRunner();
}
blink::TaskRunners task_runners(thread_label, // label
platform_runner, // platform
gpu_runner, // gpu
ui_runner, // ui
io_runner // io
);
step2:
shell_ =
Shell::Create(task_runners, // task runners
settings_, // settings
on_create_platform_view, // platform view create callback
on_create_rasterizer // rasterizer create callback
);
/flutter/shell/common/shell.cc
如果编译Engine不方便方便可在github直接查看
std::unique_ptr Shell::Create(
TaskRunners task_runners,
Settings settings,
Shell::CreateCallback on_create_platform_view,
Shell::CreateCallback on_create_rasterizer) {
PerformInitializationTasks(settings);
PersistentCache::SetCacheSkSL(settings.cache_sksl);
TRACE_EVENT0("flutter", "Shell::Create");
auto vm = DartVMRef::Create(settings);
FML_CHECK(vm) << "Must be able to initialize the VM.";
auto vm_data = vm->GetVMData();
return Shell::Create(std::move(task_runners), //
std::move(settings), //
vm_data->GetIsolateSnapshot(), // isolate snapshot
DartSnapshot::Empty(), // shared snapshot
std::move(on_create_platform_view), //
std::move(on_create_rasterizer), //
std::move(vm) //
);
}
//...
std::unique_ptr Shell::Create(
TaskRunners task_runners,
Settings settings,
fml::RefPtr isolate_snapshot,
fml::RefPtr shared_snapshot,
Shell::CreateCallback on_create_platform_view,
Shell::CreateCallback on_create_rasterizer,
DartVMRef vm) {
//...
shell = CreateShellOnPlatformThread(std::move(vm),
std::move(task_runners), //
settings, //
std::move(isolate_snapshot), //
std::move(shared_snapshot), //
on_create_platform_view, //
on_create_rasterizer //
);
//...
}
1根据setting执行了初始化Task
2创建了DartVM并将其传入Shell的Create方法中
在这里找到Shell真正的Create方法CreateShellOnPlatformThread
// Create the rasterizer on the GPU thread.
// Create the platform view on the platform thread (this thread).
// Ask the platform view for the vsync waiter. This will be used by the engine
auto vsync_waiter = platform_view->CreateVSyncWaiter();
// Create the IO manager on the IO thread.
// Create the engine on the UI thread.
根据代码注释可知:
1 在GPUThread创建rasterizer
2 在platform thread(APP主线程) 创建PlatformView 并获取其VSyncWaiter传递给Engine
3 在UIThread创建Engine
然后通过shell的Setup方法调用将platform_view、io_manager、rasterizer和engine四个unique_ptr保存到Shell对象中交由Shell对象管理并将Create的结果shell返回给AndroidShellHolder 由此Embedder层和Engine层就建立了联系 并初始化了 四个线程 一个DartVM 和一个Engine。
这里画了一个图总结一下:
Flutter与线程
Flutter Engine自己不创建, 管理线程。Flutter Engine线程的创建和管理是由embedder负责的
Embeder提供四个Task Runner, 每个Task Runner负责不同的任务,Flutter Engine不在乎Task Runner具体跑在哪个线程,但是它需要线程配置在整一个生命周期里面保持稳定。也就是说一个Runner最好始终保持在同一线程运行
Platform Task Runner
根据代码在初始化Engine的时候,将当前线程设置为PlatformTaskRunner的Thread,是Flutter和Native交互的桥梁,而且官方要求所有和Flutter的交互必须发生在PlatformThread中,因为FlutterEngine中有很多模块是非线程安全的,所以如果在其他线程调用Flutter engine会出现无法预期的异常
然是值得注意的是,PlatformThread阻塞并不会直接导致Flutter应用卡顿,因为Flutter渲染的核心在UIThread中
UI Task Runner
前文中Embedder和Engine关联后,Engine就开始着手执行Dart root isolate代码 ,Root Isolate负责创建管理的Layer Tree最终决定什么内容要绘制到屏幕上。因此这个线程的过载会直接导致卡顿掉帧。
GPU Task Runner
GPU Task Runner中的模块负责将Layer Tree提供的信息转化为实际的GPU指令。GPU Task Runner同时也负责配置管理每一帧绘制所需要的GPU资源,这包括平台Framebuffer的创建,Surface生命周期管理,保证Texture和Buffers在绘制的时候是可用的。
GPU Runner可以导致UI Runner的帧调度的延迟,GPU Runner的过载会导致Flutter应用的卡顿。一般来说用户没有机会向GPU Runner直接提交任务,因为平台和Dart代码都无法跑进GPU Runner。但是Embeder还是可以向GPU Runner提交任务的。因此建议为每一个Engine实例都新建一个专用的GPU Runner线程。
IO Task Runner
主要功能是从图片存储(比如磁盘)中读取压缩的图片格式,将图片数据进行处理为GPU Runner的渲染做好准备。
总结:
Platform Thread | GPU Thread | UI Thread | IO Thread |
---|---|---|---|
Flutter Engine的接口调用 | 执行设备GPU的指令 | 执行Dart root isolate代码 | 读取并处理图片数据 |
isolate
isolate是隔离的,每个isolate有自己的内存和单线程运行的实体. isolate之间不互相共享内存,且独立GC。
isolate中的代码是顺序执行的,且是单线程,所以不存在资源竞争和变量状态同步的问题,也就不需要锁。Dart中的并发都是多个isolate并行实现的
由于isolate不共享内存,所以isolate之间不能直接互相通信,只能通过Port进行通信,而且是异步的
Flutter Engine Runners与Dart Isolate
Dart的Isolate是Dart虚拟机自己管理的,Flutter Engine无法直接访问。Root Isolate通过Dart的C++调用能力把UI渲染相关的任务提交到UI Runner执行, 这样就可以跟Flutter Engine相关模块进行交互,Flutter UI相关的任务也被提交到UI Runner也可以相应的给Isolate一些事件通知, Dart isolate跟Flutter Runner是相互独立的,它们通过任务调度机制相互协作
DalvikVM与DartVM
DartVM的内存管理
准确的说DartVM和JVM的GC机制相似 使用分年代收集的方式,将内存分为新生代和老年代:
新生代
初次分配的对象都位于新生代中,该区域主要是存放内存较小并且生命周期较短的对象,比如局部变量。新生代会频繁执行内存回收(GC),回收采用“复制-清除”算法,将内存分为两块,运行时每次只使用其中的一块,另一块备用。当发生GC时,将当前使用的内存块中存活的对象拷贝到备用内存块中,然后清除当前使用内存块,最后,交换两块内存的角色。
老年代
老年代GC采用的是标记-清理的方式而且在标记的时候,该线程中内存区域是处于不可修改的状态,类似于JVM中stop the world,但是由于dart优秀的schedule机制和老年代GC频率很低的原因,基本上不会出现这个问题。
schedule
在查看engine源码的过程中我们看到,dartVM被作为一个变量传入了engine,为了最小化GC对APP和UI的性能影响,当FlutterApp处于空闲/无用户交互/后台的情况下,engine调用DartVM触发GC
isolate
记得在JVM中Thread是执行操作的最小单元,Dart区别于JVM的是:
isolate有自己独立的堆栈
isolate之间内存不共享
isolate之间GC不会相互影响
isolate之间不能直接相互通信,只能通过Port进行异步通信
整个Flutter Framework Widget的渲染过程都运行在一个isolate中
由此可见Dart天生不存在数据竞争和变量不同步的问,或者说Dart是单线程的,可是在移动端,APP大多数时间是处于等待的状态:等待网络请求结果,等待用户操作等因此Flutter选择Dart作为开发语言也有一定的道理。