您现在的位置是:首页 >技术教程 >Flutter 笔记 | Flutter 核心原理(六)Embedder 启动流程(Android)网站首页技术教程
Flutter 笔记 | Flutter 核心原理(六)Embedder 启动流程(Android)
Embedder
是Flutter接入原生平台的关键,其位于整个Flutter架构的底层,负责Engine
的创建、管理与销毁,同时也为Engine
提供绘制UI的接口,那么底层的实现细节如何?本文将详细分析。
Embedder关键类分析
在正式分析Embedder的启动流程之前,我们需要明确Embedder的关键类及其关系,这样在分析到具体流程时才能理解其逻辑,知晓其目的。Embedder层的关键类结构如图4-1所示。
在Embedder中,FlutterActivity
和FlutterFragment
是开发者最常接触的类,例如默认生成的Counter
App中,MainActivity
正是继承自FlutterActivity
,这两个类的父类Activity
和Fragment
是Android中常见的用于实现一屏UI的单位,因而这两个类的职责就是显示Flutter Engine绘制的UI。那么隐匿在这两个类之下的逻辑又是什么呢?
由于FlutterActivity
和FlutterFragment
的大部分逻辑和职责都是相同的,因此它们共同持有一个FlutterActivityAndFragmentDelegate
(后面内容简称Delegate
)对象,用于相同逻辑的处理,并共同实现了Host
接口用于自身的功能抽象。
Delegate
持有两个关键的类:FlutterEngine
和FlutterView
,前者负责 Flutter Engine (libflutter.so
)在 Embedder 中的调用和管理,而后者则负责 Flutter Engine 中UI数据的上屏显示。FlutterView
也会用到Engine的功能,因此也持有FlutterEngine
。
此外,FlutterView
还持有RenderSurface
的具体实现。RenderSurface
顾名思义就是渲染 Flutter UI 的接口,它有3个实现:
FlutterSurfaceView
基于 Android 的SurfaceView
实现,性能最佳,但它不在 Android 的 View Hierarchy 中,一般用于整页的 Flutter UI 展示,默认优先使用;FlutterTextureView
基于 Android 的TextureView
实现,性能不如前者,但是使用体验更接近 Android 中一个普通的View
,比较适合一些Flutter嵌入原生UI的场景;FlutterImageView
通常用于存在Platform View的场景中。
RenderSurface
会通过FlutterRender
对象调用 Engine 的相关绘制能力,FlutterRender
对 Engine 的绘制能力做了抽象封装,便于 Embedder 使用。此外FlutterEngine
会通过DartExecutor
调用Engine 中 Dart Runtime 相关的逻辑。这两个类中,RenderSurface
负责UI相关工作,DartExecutor
负责逻辑相关的工作,但它们最终都要调用Engine的具体Native方法,因而都会持有FlutterJNI
对象,该对象集中了大部分Embedder(Java代码)和 Engine(C++代码)的相互调用接口。
后面内容中将多次出现以下几个概念:Surface、SurfaceTexture、SurfaceView、TextureView
。为避免混淆,在此统一说明。
Surface
是一个比较抽象的概念,表示一块渲染缓冲区的句柄(Handle),它通常由渲染数据的消费者(比如SurfaceTexture、MediaRecorder
)创建,并作为参数传递给渲染数据的生产者(比如OpenGL ES
)。SurfaceTexture
是Surface
和OpenGL ES
纹理的组合,即OpenGL ES
通过绘制指令生产的纹理需要一个输出,而SurfaceTexture
正是一个典型的渲染输出。SurfaceTexture
内部包含一个BufferQueue
实例,负责连接渲染数据的生产者和消费者。通常情况下,BufferQueue
会以OpenGL ES
等作为生产者,以TextureView
等作为消费者。TextureView
类结合了View
和SurfaceTexture
,它可以消费SurfaceTexture
中的纹理数据,并通过重写View
的draw
方法的形式显示到屏幕中。SurfaceView
在Android API中的出现时间比TextureView
更早,它在使用上和普通的View
一致,但在底层却拥有自己独立的Surface
,这样的好处是对这个Surface
的渲染可以放到单独线程去做,渲染时可以有自己的上下文环境。这对于一些游戏、视频等性能相关的应用非常有益,因为它不会影响主线程对事件的响应。但它也有缺点,因为这个Surface
不在默认的View Tree
中,它的显示也不受View
的属性控制,所以不能进行平移、缩放等变换,也不能放在其他ViewGroup
中,一些View
中的特性也无法使用。
总的来说,TextureView
虽然灵活,但是性能更低,SurfaceView
虽然存在一些限制,但是性能更加高效。对 Flutter UI 而言,如果是一个独立的、全屏的界面(默认情况),应该优先使用SurfaceView
作为渲染的输出。
此外,Flutter中还有一种使用TextureLayer
展示Platform View
的虚拟显示模式,称为Virtual Display,它仅在Android平台支持。
以上内容从空间的角度自顶向下、由表及里分析了Embedder的结构,接下来将从时间的角度分析Embedder在整个Flutter启动流程中所扮演的角色和发挥的作用(注:后面大部分内容都将遵循此顺序,先分析空间结构,后分析时序流程)。
启动准备阶段
这里以flutter create
命令默认创建的Counter
App为例进行介绍。启动后首先会触发FlutterApplication
中的onCreate
回调,如代码清单4-1所示。
// 代码清单4-1 engine/shell/platform/android/io/flutter/app/FlutterApplication.java
public class FlutterApplication extends Application {
@Override
@CallSuper
public void onCreate() {
super.onCreate();
FlutterInjector.instance().flutterLoader().startInitialization(this);// 注意,这里依赖注入式的设计,通过解耦以提升代码的可测试性
}
}
以上逻辑最终将调用FlutterLoader
的startInitialization
方法,如代码清单4-2所示。
// 代码清单4-2 engine/shell/platform/android/io/flutter/embedding/engine/loader/FlutterLoader.java
public void startInitialization(@NonNull Context applicationContext) {
startInitialization(applicationContext, new Settings());
}
public void startInitialization(@NonNull Context applicationContext, @NonNull
Settings settings) {
if (this.settings != null) { return; } // 防止多次初始化
if (Looper.myLooper() != Looper.getMainLooper()) { throw ... } // 检查是否在主线程
// 确保使用的是Global Context,如果使用某个Activity的Context,可能导致内存泄漏
final Context appContext = applicationContext.getApplicationContext();
this.settings = settings;
initStartTimestampMillis = SystemClock.uptimeMillis(); // 用于统计启动耗时
flutterApplicationInfo = ApplicationInfoLoader.load(appContext); // 加载Manifest信息
VsyncWaiter.getInstance((WindowManager) appContext // 初始化Vsync监听
.getSystemService(Context.WINDOW_SERVICE)).init(); // 见代码清单4-3
Callable<InitResult> initTask = ...... // 见代码清单4-4
initResultFuture = Executors.newSingleThreadExecutor().submit(initTask);
// 开始执行
}
以上逻辑中,首先会检查是否设置settings
变量,以防止多次初始化。这里没有做多线程检查,原因是Flutter的初始化必须在主线程进行,否则会抛出异常。这里之所以要在主线程初始化主要是因为Embedder的主要职责就是为Engine提供绘制UI的接口,而Android中UI相关的操作必须在主线程进行,后面内容中将发现Flutter中Dart的逻辑即渲染管道中3棵树的构建其实都不是在Android的主线程中进行的。
接下来会获取ApplicationContext
对象用于后面内容获取ApplicationInfo
和SystemService
,这里有一个细节就是强制调用getApplicationContext
以确保使用的是Application
的Context
。如果直接使用Activity
等组件的Context
会存在内存泄漏的风险,这是因为SystemService
会持有调用者的强引用。接下来会完成VsyncWaiter
的初始化,具体逻辑如代码清单4-3所示。最后会初始化一个异步的Task
任务,并立即启动执行,具体逻辑如代码清单4-4所示。
// 代码清单4-3 engine/shell/platform/android/io/flutter/view/VsyncWaiter.java
public void init() {
FlutterJNI.setAsyncWaitForVsyncDelegate(asyncWaitForVsyncDelegate);
// 见代码清单5-31
float fps = windowManager.getDefaultDisplay().getRefreshRate();
FlutterJNI.setRefreshRateFPS(fps);
}
以上逻辑的核心是初始化并赋值给FlutterJNI
一个AsyncWaitForVsyncDelegate
对象,该对象将被Engine主动调用,使用场景是:Engine有一帧UI需要渲染时并不会立即执行,而是会通过Embedder注册一个监听,等到下一个Vsync信号到达后再启动渲染。
下面继续分析initTask
变量的具体内容。
// 代码清单4-4 engine/shell/platform/android/io/flutter/embedding/engine/loader/FlutterLoader.java
Callable<InitResult> initTask =
new Callable<InitResult>() {
@Override
public InitResult call() {
ResourceExtractor resourceExtractor = initResources(appContext);
flutterJNI.loadLibrary(); // 加载libflutter.so,触发JNI_OnLoad,见代码清单4-20
Executors.newSingleThreadExecutor().execute(
new Runnable() { // 异步预初始化字体管理器
@Override public void run() {
flutterJNI.prefetchDefaultFontManager();
}
});
if (resourceExtractor != null) { // 阻塞当前线程
resourceExtractor.waitForCompletion();
}
return new InitResult(
PathUtils.getFilesDir(appContext),
PathUtils.getCacheDirectory(appContext),
PathUtils.getDataDirectory(appContext));
}
};
以上逻辑中,首先会初始化一个ResourceExtractor
对象,用于资源的提取,具体如代码清单4-5所示。其次会加载libflutter.so
,即Flutter Engine
,该方法会触发一个系统回调,即JNI_OnLoad
,这是Engine在启动流程中触发的第1个逻辑。最后该线程会阻塞直至完成ResourceExtractor
对象的任务。
// 代码清单4-5 engine/shell/platform/android/io/flutter/embedding/engine/loader/FlutterLoader.java
private ResourceExtractor initResources(@NonNull Context applicationContext) {
ResourceExtractor resourceExtractor = null;
// 注意,只有Debug/JIT模式下构建产物才需要提取逻辑,Release模式产物形式为libapp.so
// 可直接进行动态链接,详见后面内容分析
if (BuildConfig.DEBUG || BuildConfig.JIT_RELEASE) { // 初始化变量
final String dataDirPath = PathUtils.getDataDirectory(applicationContext);
final String packageName = applicationContext.getPackageName();
final PackageManager packageManager = applicationContext.getPackageManager();
final AssetManager assetManager = applicationContext.getResources().getAssets();
resourceExtractor =
new ResourceExtractor(dataDirPath, packageName, packageManager, assetManager);
// Flutter SDK和业务代码构建的产物(Kernel)被提取到文件,Flutter Engine负责
resourceExtractor // 映射到内存,assets目录下的文件无法直接映射,所以需要提取
.addResource(fullAssetPathFrom(flutterApplicationInfo.vmSnapshotData))
.addResource(fullAssetPathFrom(flutterApplicationInfo.isolateSnapshotData))
.addResource(fullAssetPathFrom(DEFAULT_KERNEL_BLOB));
resourceExtractor.start(); // 本质上是启动一个AsyncTask
}
return resourceExtractor;
}
以上逻辑是ResourceExtractor
的初始化逻辑,主要作用是在Debug
和JIT
模式下提取assets
目录下的资源文件到内部存储中,这是因为以上两种模式中Flutter SDK和业务代码将被构建成Kernel
格式的二进制文件,Engine将通过文件内存映射的方式进行加载,而assets
本质上还是zip
压缩包的一部分,没有自己的物理路径,因而要在Engine的初始化逻辑之前完成相关资源的提取并返回真实的物理路径,具体的提取复制逻辑还会做一些时间戳的校验,在此不再赘述。以上文件资源的使用将在代码清单4-68中详细分析。
总结一下 FlutterApplication
的onCreate
中主要做的工作就是三件事:
- 给
FlutterJNI
一个AsyncWaitForVsyncDelegate
对象,以便后续Engine可以向系统注册Vsync
信号监听驱动Flutter渲染管线的Frame绘制流程 - 加载
libflutter.so
,触发JNI_OnLoad
- 初始化
ResourceExtractor
,用以提取Asset
资源
FlutterEngine 初始化
注意
FlutterEngine
指 Flutter Embedder 中的Java
类,Flutter Engine 指libflutter.so
所对应的逻辑,是相对于 Flutter Embedder 和 Flutter Framework 而言的。
接下来,主线程会进入FlutterActivity
的生命周期回调,首先是onCreate
,具体逻辑如代码清单4-6所示。
// 代码清单4-6 engine/shell/platform/android/io/flutter/embedding/android/FlutterActivity.java
protected void onCreate(@Nullable Bundle savedInstanceState) {
switchLaunchThemeForNormalTheme(); // 主题切换
super.onCreate(savedInstanceState);
delegate = new FlutterActivityAndFragmentDelegate(this);
delegate.onAttach(this); // 创建FlutterEngine
delegate.onRestoreInstanceState(savedInstanceState); // 状态存储入口
lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_CREATE); // 生命周期
configureWindowForTransparency(); // 配置窗口
setContentView(createFlutterView()); // 创建FlutterView,并设为根View,见代码清单4-12
configureStatusBarForFullscreenFlutterExperience(); // 全屏状态栏
}
以上逻辑中,switchLaunchThemeForNormalTheme
方法负责从闪屏页的主题切换到主页面的主题,然后新建一个Delegate
对象,FlutterActivity
将自身的大多数逻辑都委托给该对象,onAttach
方法将完成FlutterEngine
的初始化。configureWindowForTransparency
方法负责配置透明主题,createFlutterView
方法将调用FlutterActivityAndFragmentDelegate
的onCreateView
方法,并返回Flutter实际进行绘制的View
作为Activity
的根View,后面将详细分析这部分内容。
首先分析onAttach
方法,如代码清单4-7所示。
// 代码清单4-7 engine/shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java
void onAttach(@NonNull Context context) {
ensureAlive();
if (flutterEngine == null) {
setupFlutterEngine(); // 初始化FlutterEngine,见代码清单4-8
}
if (host.shouldAttachEngineToActivity()) { // 通知Activity Attach完成
flutterEngine.getActivityControlSurface()
.attachToActivity(this, host.getLifecycle());
}
// PlatformPlugin会提供宿主基础的原生能力,如剪贴板
platformPlugin = host.providePlatformPlugin(host.getActivity(), flutterEngine);
// 为Host提供一个感知FlutterEngine初始化完成的时机
host.configureFlutterEngine(flutterEngine); // 开发者一般在此进行Platform Channel注册等操作
}
以上逻辑负责初始化FlutterEngine
,其在Embedder中表现为实例化一个FlutterEngine
对象并完成其相关成员变量的配置,后面内容将详细分析。接着会完成PlatformPlugin
的创建,FlutterActivity
提供了默认的实现。然后通过attachToActivity
方法告知所有实现了ActivityControlSurface
接口的插件,其所注册的FlutterEngine
已经完成和宿主Activity
的绑定(Attach
)。最后通过configureFlutterEngine
方法给FlutterActivity
提供一个回调,开发者通常需要在该回调中完成涉及FlutterEngine
的初始化工作,比如Platform View、Platform Channel
的注册。
接下来深入分析FlutterEngine
的创建过程,即setupFlutterEngine
方法的逻辑,如代码清单4-8所示。
// 代码清单4-8 engine/shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java
void setupFlutterEngine() {
String cachedEngineId = host.getCachedEngineId();
if (cachedEngineId != null) { // 第1步,如果指明使用缓存,则尝试获取
flutterEngine = FlutterEngineCache.getInstance().get(cachedEngineId);
isFlutterEngineFromHost = true;
if (flutterEngine == null) { throw new IllegalStateException .....} // 找不到缓存,抛出异常
return;
}
flutterEngine = host.provideFlutterEngine(host.getContext());
if (flutterEngine != null) { // 第2步,如果Host提供了Engine,则直接使用
isFlutterEngineFromHost = true;
return;
}
flutterEngine = new FlutterEngine( // 第3步,新建一个FlutterEngine实例
host.getContext(),
host.getFlutterShellArgs().toArray(), // 外部参数
/*automaticallyRegisterPlugins=*/ false,
host.shouldRestoreAndSaveState());
isFlutterEngineFromHost = false;
}
以上逻辑分3种情况完成FlutterEngine
的初始化。
- 第1种情况,对于提供了缓存
id
的Host
,会查找id
对应的FlutterEngine
,如果没有则抛出异常,而不是继续后面的兜底逻辑。 - 第2种情况,对于
Host
自己提供FlutterEngine
实例的情况,直接使用即可。 - 第3种情况,如果以上两种情况都不满足则会新建一个
FlutterEngine
实例。
由于前两种情况FlutterEngine
的具体实例都可以由Host
控制(通过实现getCachedEngineId
或者provideFlutterEngine
),因此其isFlutterEngineFromHost
字段为true
。
注意:对于提供了
CachedEngineId
却获取FlutterEngine
失败的场景,如果不抛出异常,继续下面的逻辑,虽然可以保证一定可以成功设置一个FlutterEngine
实例,但是使用者的逻辑可能依赖于缓存的FlutterEngine
对象的一些特有功能,因此不抛出异常而进行兜底实际上是隐藏了风险(使用者期望获得和实际返回的Engine
不一致),增加了后续排查问题的难度,所以这里直接抛出了异常。
一般情况下,默认逻辑是新建一个FlutterEngine
实例,其最终将触发的构造函数如代码清单4-9所示。
// 代码清单4-9 engine/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java
public FlutterEngine( ...... ) {
AssetManager assetManager;
try { // 优先使用一个独立的AssetManager
assetManager = context.createPackageContext(context.getPackageName(), 0).
getAssets();
} catch (NameNotFoundException e) {
assetManager = context.getAssets(); // 若失败则使用当前的
} // 第1步,DartExecutor创建,负责Embedder和Framework的通信
this.dartExecutor = new DartExecutor(flutterJNI, assetManager);
// 设置FlutterJNI中处理Framework消息的PlatformMessageHandler
this.dartExecutor.onAttachedToJNI();
DeferredComponentManager deferredComponentManager =
FlutterInjector.instance().deferredComponentManager();
// 第2步,提供给Framework的平台能力的封装
accessibilityChannel = new AccessibilityChannel(dartExecutor, flutterJNI);
deferredComponentChannel = new DeferredComponentChannel(dartExecutor);
// SKIP
this.localizationPlugin = new LocalizationPlugin(context, localizationChannel);
this.flutterJNI = flutterJNI;
if (flutterLoader == null) {
flutterLoader = FlutterInjector.instance().flutterLoader();
}
if (!flutterJNI.isAttached()) { // 第3步,FlutterEngine的启动逻辑
flutterLoader.startInitialization(context.getApplicationContext()); // 见代码清单4-2
flutterLoader.ensureInitializationComplete(context, dartVmArgs); // 见代码清单4-10
} // 第4步,设置FlutterJNI的关键成员字段
flutterJNI.addEngineLifecycleListener(engineLifecycleListener);
flutterJNI.setPlatformViewsController(platformViewsController);
flutterJNI.setLocalizationPlugin(localizationPlugin);
flutterJNI.setDeferredComponentManager(
FlutterInjector.instance().deferredComponentManager());
if (!flutterJNI.isAttached()) { // 至此,Embedder侧完全准备好和Engine交互
attachToJni(); // 建立Embedder和Engine的连接,见代码清单4-24
} // 第5步,Engine绘制相关能力的初始化
this.renderer = new FlutterRenderer(flutterJNI); // 该类代表了Engine绘制相关的能力
this.platformViewsController = platformViewsController;
this.platformViewsController.onAttachedToJNI();
this.pluginRegistry = new FlutterEngineConnectionRegistry(
context.getApplicationContext(), this, flutterLoader);
if (automaticallyRegisterPlugins) {
registerPlugins(); // 自动完成插件的注册
}
}
以上逻辑中,
- 第1步会完成
DartExecutor
的创建,该对象将负责Embedder和Framework的通信。 - 第2步会完成诸多平台功能封装类的初始化,它们大多是
Platform Channel
的二次封装,在此不再赘述。 - 第3步会通过
ensureInitializationComplete
方法完成FlutterEngine
的初始化参数的配置,注意,此时FlutterEngine
中大部分关键类都尚未开始创建,因为Embedder的FlutterJNI
还没有完成初始化。 - 第4步会对
FlutterJNI
对象的相关成员进行初始化,并调用attachJni
方法完成Embedder和Engine的绑定,Engine的大部分逻辑由attachJni
方法触发,后面将详细分析这部分内容。 - 第5步会完成
FlutterRenderer
的初始化以及Platform View
和Plugin
的相关初始化工作。
下面继续分析FlutterEngine
的初始化工作,如代码清单4-10所示。
// 代码清单4-10 engine/shell/platform/android/io/flutter/embedding/engine/loader/FlutterLoader.java
public void ensureInitializationComplete(
@NonNull Context applicationContext, @Nullable String[] args) {
if (initialized) { return; } // 已经完成
if (Looper.myLooper() != Looper.getMainLooper()) { ...... }
if (settings == null) { ...... } // 没有调用startInitialization,退出
try {
InitResult result = initResultFuture.get(); // 阻塞至代码清单4-4完成
List<String> shellArgs = new ArrayList<>(); // 开始拼接各种启动参数
// SKIP
if (args != null) { Collections.addAll(shellArgs, args); } // 透传外部参数
String kernelPath = null;
if (BuildConfig.DEBUG || BuildConfig.JIT_RELEASE) {
// SKIP Kernel Snapshot的名称及位置
} else { // Release模式,so文件的名称,默认为libapp.so
shellArgs.add("--" + AOT_SHARED_LIBRARY_NAME
+ "=" + flutterApplicationInfo.aotSharedLibraryName);
shellArgs.add("--" + AOT_SHARED_LIBRARY_NAME // 兜底逻辑,传入完整路径
+ "=" + flutterApplicationInfo.nativeLibraryDir
+ File.separator + flutterApplicationInfo.aotSharedLibraryName);
}
// SKIP 其他各种参数
long initTimeMillis = SystemClock.uptimeMillis() - initStartTimestampMillis;
flutterJNI.init(applicationContext, shellArgs.toArray(new String[0]), // 见代码清单4-23
kernelPath, result.appStoragePath, result.engineCachesPath,
initTimeMillis); // Embedder初始化至此的耗时
initialized = true; // FlutterLoader 初始化完成
} catch (Exception e) { ...... }
}
以上逻辑中,主要是初始化一系列参数,并通过JNI
调用传递给Engine
。其中,需要关注的是Framework
和相关Dart
业务代码的加载情况。在Debug
模式下会加载 3 个字段,其具体值及作用如下:
-
SNAPSHOT_ASSET_PATH_KEY
(snapshot-asset-path
):默认值为flutter_assets
,表示下面两个资源的所在路径。 -
VM_SNAPSHOT_DATA_KEY
(vm-snapshot-data
):默认值为vm_snapshot_data
,表示vm isolate
的数据段和代码段。 -
ISOLATE_SNAPSHOT_DATA_KEY
(isolate-snapshot-data
):默认值为isolate_snapshot_data
,表示主isolate
(业务代码及其所依赖的Framework
)的数据段和代码段。
而在Release
模式下则只会加载AOT_SHARED_LIBRARY_NAME
,其默认值为libapp.so
,其相当于vm_snapshot_data
和isolate_snapshot_data
的和,只是前者是Release
模式的AOT
产物,而后两者则是Debug
模式下Kernel
文件序列化的产物,libapp.so
的本质是一个elf
格式的文件,因而可以通过相关工具查看其内容,如代码清单4-11所示。
代码清单4-11 app.so
文件的内部结构
$ greadelf -s --wide libapp.so
Symbol table '.dynsym' contains 6 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
1: 00001000 12 FUNC GLOBAL DEFAULT 1 _kDartBSSData
2: 00002000 9616 FUNC GLOBAL DEFAULT 2 _kDartVmSnapshotInstructions
3: 00005000 24400 FUNC GLOBAL DEFAULT 3 _kDartVmSnapshotData
4: 0000b000 0x1ed9d8 FUNC GLOBAL DEFAULT 4 _kDartIsolateSnapshotInstructions
5: 001f9000 0x175860 FUNC GLOBAL DEFAULT 5 _kDartIsolateSnapshotData
以上信息中,_kDartIsolateSnapshotInstructions
中包含了大部分的逻辑信息;_kDartIsolateSnapshotData
中包含了大部分的数据信息。
总的来说,在FlutterEngine
初始化过程中会触发FlutterJNI
中的两个关键Engine调用——nativeInit
方法和nativeAttach
方法。
总结
其中,
setupFlutterEngine() 的逻辑:
- 若指定使用缓存,则缓存获取 FlutterEngine,获取不到抛异常
- 获取宿主Host提供的 FlutterEngine,获取到直接使用
- 否则,创建一个 FlutterEngine 实例
FlutterEngine 构造函数的逻辑:
- 创建 DartExecutor
- 给 Flutter Framework 提供平台能力的封装
- 配置 FlutterEngine 的初始化参数
- FlutterJNI 初始化
- FlutterRenderer等绘制能力相关初始化
FlutterView 初始化
在完成FlutterEngine
的初始化之后会进行FlutterView
的初始化,如代码清单4-12所示。
// 代码清单4-12 engine/shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java
View onCreateView(LayoutInflater inflater,
@Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
ensureAlive();
if (host.getRenderMode() == RenderMode.surface) { // 第1步,判断Host所提供的RenderMode
FlutterSurfaceView flutterSurfaceView = new FlutterSurfaceView( // 见代码清单4-13
host.getActivity(), host.getTransparencyMode() == TransparencyMode.transparent);
// 为Host预留一个接口,用于在FlutterSurfaceView创建完成后提供自定义配置入口
host.onFlutterSurfaceViewCreated(flutterSurfaceView);
flutterView = new FlutterView(host.getActivity(), flutterSurfaceView);
} else { ...... } // SKIP FlutterTextureView逻辑类似
// 第2步,间接为Host提供首帧渲染/停止渲染的回调
flutterView.addOnFirstFrameRenderedListener(flutterUiDisplayListener);
flutterSplashView = new FlutterSplashView(host.getContext()); // 第3步,闪屏相关逻辑
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
flutterSplashView.setId(View.generateViewId());
} else {
flutterSplashView.setId(486947586);
} // 开始渲染首帧,见代码清单4-15
flutterSplashView.displayFlutterViewWithSplash(flutterView, host.provide
SplashScreen());
// 注意,FlutterEngine和FlutterView建立连接,这是后面能够渲染的基础
flutterView.attachToFlutterEngine(flutterEngine); // 第4步,见代码清单4-17
return flutterSplashView;
}
以上逻辑可大致分为4步:
- 第1步判断
Host
所提供的RenderMode
,一般默认是Surface
,因而会初始化一个FlutterSurfaceView
的实例,并以此为参数初始化FlutterView
。该过程的核心逻辑如代码清单4-13和代码清单4-14所示。 - 第2步向
FlutterView
注册一个flutterUiDisplayListener
对象,用于向Host
提供首帧渲染以及停止渲染的回调,相关调用链可自行分析。 - 第3步完成闪屏的初始化,并调用
displayFlutterViewWithSplash
方法用于从闪屏到首帧的过渡,相关逻辑如代码清单4-15所示。 - 第4步中
FlutterView
会调用attachToFlutterEngine
方法完成最后的初始化工作,相关逻辑如代码清单4-17所示。
接下来依次分析前面内容提及的4个步骤。首先是FlutterSurfaceView
的初始化,其最终会调用init
方法,如代码清单4-13所示。
// 代码清单4-13 engine/shell/platform/android/io/flutter/embedding/android/FlutterSurfaceView.java
private void init() {
if (renderTransparently) { // 透明
getHolder().setFormat(PixelFormat.TRANSPARENT);
setZOrderOnTop(true);
} // 监听Embedder中Surface的状态变化,在回调中调用Engine对应的处理逻辑
getHolder().addCallback(surfaceCallback);
setAlpha(0.0f); // 保持透明,防止黑屏
}
private final SurfaceHolder.Callback surfaceCallback =
new SurfaceHolder.Callback() {
@Override // Embedder中,在Surface完成创建之后触发
public void surfaceCreated(@NonNull SurfaceHolder holder) {
isSurfaceAvailableForRendering = true;
if (isAttachedToFlutterRenderer) {
connectSurfaceToRenderer(); // 通过本回调通知Engine开始渲染UI,见代码清单4-18
}
}
@Override public void surfaceChanged( ...... ) { ...... }
@Override public void surfaceDestroyed( ...... ) { ...... }
};
以上逻辑会为当前Surface
添加一个回调,用于在Surface
创建完成后触发和FlutterRender
的绑定,其中isAttachedToFlutterRenderer
字段表示RederSurface
是否绑定到FlutterRender
,默认为false
,设置为true
的时机将在代码清单4-18中介绍。connectSurfaceToRenderer
方法也将在后面内容详细分析。
以上是FlutterSurfaceView
的初始化逻辑。接着分析FlutterView
的初始化逻辑,核心逻辑也在自身的init
方法中,如代码清单4-14所示。
// 代码清单4-14 flutter/shell/platform/android/io/flutter/embedding/android/FlutterView.java
private void init() {
if (flutterSurfaceView != null) { // 默认情况下用于一个完整独立的Flutter UI
addView(flutterSurfaceView);
} else if (flutterTextureView != null) { // Flutter UI作为原生UI的一部分时
addView(flutterTextureView);
} else { // Flutter UI作为宿主,Platform View作为其一部分
addView(flutterImageView);
}
setFocusable(true);
setFocusableInTouchMode(true);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
setImportantForAutofill(View.IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS);
}
}
以上是FlutterView
的初始化逻辑,FlutterView
是FrameLayout
的子类,它会选择RenderSurface
的具体实现之一作为自己的子View
,并设置焦点、自动填充等属性。FlutterView
初始化完成后,将会执行其展示逻辑,如代码清单4-15所示。
// 代码清单4-15 engine/shell/platform/android/io/flutter/embedding/android/FlutterSplashView.java
public void displayFlutterViewWithSplash(@NonNull FlutterView flutterView, @Nullable SplashScreen splashScreen) {
if (this.flutterView != null) { // 清理之前的FlutterView
this.flutterView.removeOnFirstFrameRenderedListener(flutterUiDisplayListener);
removeView(this.flutterView);
}
if (splashScreenView != null) { // 清理之前的闪屏
removeView(splashScreenView);
}
this.flutterView = flutterView;
addView(flutterView); // 显示FlutterView
this.splashScreen = splashScreen;
if (splashScreen != null) { // 存在闪屏资源
if (isSplashScreenNeededNow()) { // 立即显示闪屏,并通过Listener触发切换
splashScreenView = splashScreen.createSplashView(getContext(),
splashScreenState);
addView(this.splashScreenView); // 加入View Tree
flutterView.addOnFirstFrameRenderedListener(flutterUiDisplayListener); // 见代码清单4-16
} else if (isSplashScreenTransitionNeededNow()) { // 立即进入:从闪屏切换到FlutterView状态
splashScreenView = splashScreen.createSplashView(getContext(),
splashScreenState);
addView(splashScreenView); // 显示闪屏
transitionToFlutter(); // 然后立即开始切换到FlutterView
} else if (!flutterView.isAttachedToFlutterEngine()) { // 见代码清单4-16
// 若当前FlutterView尚未与FlutterEngine绑定,则监听其绑定状态,完成绑定时触发回调
flutterView.addFlutterEngineAttachmentListener(flutterEngineAttachmentListener);
}
} // 若不存在闪屏资源,则一直显示FlutterView
}
private void transitionToFlutter() {
transitioningIsolateId = flutterView.getAttachedFlutterEngine().
getDartExecutor().getIsolateServiceId();
splashScreen.transitionToFlutter(onTransitionComplete);
}
以上逻辑中,FlutterView
表示最终将显示UI的View
,splashScreenView
表示首帧渲染前显示的闪屏UI,其也继承自FrameLayout
。displayFlutterViewWithSplash
方法首先会完成两者的清理工作,其次将FlutterView
添加到布局中。如果存在闪屏资源,则会分成如下3种情况进行处理。
-
当前需要展示闪屏:将
splashScreenView
添加到布局中,并注册flutterUiDisplayListener
用于在首帧渲染后触发切换,具体逻辑如代码清单4-16所示。 -
当前需要从闪屏切换到首帧:将
splashScreenView
添加到布局中,并立即触发切换到首帧的动画。 -
FlutterView
暂时还没有绑定(attached
)到FlutterEngine
:为FlutterView
注册一个flutterEngineAttachmentListener
监听器,用于FlutterView
调用attachToFlutterEngine
方法时触发,这部分内容将在代码清单4-17中详细介绍。
flutterUiDisplayListener
和flutterEngineAttachmentListener
的具体逻辑见代码清单4-16。
// 代码清单4-16 engine/shell/platform/android/io/flutter/embedding/android/FlutterSplashView.java
@NonNull
private final FlutterView.FlutterEngineAttachmentListener
flutterEngineAttachmentListener = new FlutterView.FlutterEngineAttachmentListener() {
@Override // FlutterView和FlutterEngine绑定成功后触发
public void onFlutterEngineAttachedToFlutterView(@NonNull FlutterEngine engine) {
flutterView.removeFlutterEngineAttachmentListener(this);
displayFlutterViewWithSplash(flutterView, splashScreen); // 见代码清单4-15
}
@Override
public void onFlutterEngineDetachedFromFlutterView() {}
};
@NonNull
private final FlutterUiDisplayListener flutterUiDisplayListener =
new FlutterUiDisplayListener() {
@Override // FlutterView开始渲染时触发
public void onFlutterUiDisplayed() {
if (splashScreen != null) {
transitionToFlutter(); // Flutter UI开始渲染时从闪屏开始变换
}
}
@Override public void onFlutterUiNoLongerDisplayed() { }
};
以上逻辑中,onFlutterUiDisplayed
即首帧完成渲染的时机,该回调中会触发前面内容提到的transitionToFlutter
,用于触发从闪屏到首帧的过渡动画。onFlutterEngineAttachedToFlutterView
即FlutterView
和FlutterEngine
完成绑定的时机,该回调中将会再次触发displayFlutterViewWithSplash
的逻辑。
下面分析FlutterView
和FlutterEngine
的绑定过程,其发起点位于代码清单4-12的第4步,即attachToFlutterEngine
方法,其逻辑如代码清单4-17所示。
// 代码清单4-17 engine/shell/platform/android/io/flutter/embedding/android/FlutterView.java
public void attachToFlutterEngine(@NonNull FlutterEngine flutterEngine) {
Log.v(TAG, "Attaching to a FlutterEngine: " + flutterEngine);
if (isAttachedToFlutterEngine()) {
if (flutterEngine == this.flutterEngine) { return; }
detachFromFlutterEngine(); // 解除绑定
}
this.flutterEngine = flutterEngine;
// 获取FlutterEngine对于FlutterEngine绘制能力的封装:FlutterRender
FlutterRenderer flutterRenderer = this.flutterEngine.getRenderer();
isFlutterUiDisplayed = flutterRenderer.isDisplayingFlutterUi();
// FlutterView和FlutterEngine进行绑定的本质是RenderSurface与FlutterRender的attach
renderSurface.attachToRenderer(flutterRenderer); // 见代码清单4-18
flutterRenderer.addIsDisplayingFlutterUiListener(flutterUiDisplayListener); // 对外通知
// SKIP 创建和初始化各种与UI相关的桥接功能类
sendUserSettingsToFlutter(); // 发送与屏幕相关的配置给Engine
localizationPlugin.sendLocalesToFlutter(getResources().getConfiguration());
sendViewportMetricsToFlutter(); // 发送窗口大小、设备分辨率等信息
flutterEngine.getPlatformViewsController().attachToView(this);
for (FlutterEngineAttachmentListener listener : flutterEngineAttachmentListeners) {
listener.onFlutterEngineAttachedToFlutterView(flutterEngine); // 通知绑定完成
}
if (isFlutterUiDisplayed) { // 如果已经在展示Flutter UI,则直接触发回调
flutterUiDisplayListener.onFlutterUiDisplayed();
}
}
由代码清单4-12可知,attachToFlutterEngine
方法在displayFlutterViewWithSplash
逻辑之后。因此,存在闪屏时通常会先触发代码清单4-15中的第3种情况,然后触发第1种情况。下面开始分析attachToFlutterEngine
方法自身的逻辑。
以上逻辑主要是FlutterView
和FlutterEngine
的绑定,共分为3步。
- 第1步是
FlutterView
中各成员的赋值。 - 第2步是
RenderSurface
和FlutterRender
的绑定,具体如代码清单4-18所示,这部分内容将在后面详细分析。 - 第3步会完成各种平台能力、
Platform View
的初始化,sendUserSettingsToFlutter
和sendViewportMetricsToFlutter
会将平台属性等信息通过FlutterJNI
打包发送给Engine
,在此不再赘述。
最后将会触发绑定完成的通知,用于前面内容的闪屏逻辑触发等操作。
下面详细分析RenderSurface
和FlutterRender
的绑定。这里以FlutterSurfaceView
为例,如代码清单4-18所示。
// 代码清单4-18 engine/shell/platform/android/io/flutter/embedding/android/FlutterSurfaceView.java
public void attachToRenderer(@NonNull FlutterRenderer flutterRenderer) {
Log.v(TAG, "Attaching to FlutterRenderer.");
if (this.flutterRenderer != null) {
this.flutterRenderer.stopRenderingToSurface();
this.flutterRenderer.removeIsDisplayingFlutterUiListener(flutterUiDisplayListener);
}
this.flutterRenderer = flutterRenderer;
isAttachedToFlutterRenderer = true; // 开始监听FlutterRender何时渲染出首帧
this.flutterRenderer.addIsDisplayingFlutterUiListener(flutterUiDisplayListener);
if (isSurfaceAvailableForRendering) { // Surface已经可用,一般这里为false
connectSurfaceToRenderer(); // 通常由代码清单4-13的surfaceCallback触发
}
}
private final FlutterUiDisplayListener flutterUiDisplayListener =
new FlutterUiDisplayListener() {
@Override // Engine渲染首帧时通过FlutterJNI的onFirstFrame方法触发
public void onFlutterUiDisplayed() {
setAlpha(1.0f); // Engine开始完全接管当前Surface的渲染
if (flutterRenderer != null) {
flutterRenderer.removeIsDisplayingFlutterUiListener(this);
}
}
@Override public void onFlutterUiNoLongerDisplayed() { }
};
private void connectSurfaceToRenderer() {
if (flutterRenderer == null || getHolder() == null) {
throw new IllegalStateException(" ...... ");
} // 告知Engine可以开始渲染
flutterRenderer.startRenderingToSurface(getHolder().getSurface());
}
以上逻辑和FlutterView
的attachToFlutterEngine
方法类似,而FlutterSurfaceView
是FlutterView
中实际负责渲染的类,FlutterRender
是FlutterEngine
中对底层渲染能力的抽象封装,因此这里的逻辑主要是与渲染相关的。
对Flutter的渲染而言,有两个重要时机:一个是Embedder的Surface
创建完成(由Embedder决定);另一个是Engine的首帧数据准备好(由Engine
决定)。flutterUiDisplayListener
负责监听Engine的首帧数据准备完成时机,并在此时将自身透明度设置为1
,表明自身将完全接管当前Surface
的渲染。isSurfaceAvailableForRendering
方法用于判断Surface
是否准备完成,通常这里为false
。connectSurfaceToRenderer
通常在Surface
的创建回调中触发,即在代码清单4-13所示的逻辑中,其自身逻辑最终会调用nativeSurfaceCreated
方法,用于通知Engine侧:Embedder中的Surface
已经准备完毕,可以进行首帧渲染。
需要注意的是,以上大部分逻辑实际上是基于回调触发的,有以下两个关键节点。
-
(1)Embedder创建
Surface
,在创建完成的回调中告知Engine
开始渲染。 -
(2)
Engine
基于Surface
开始渲染,并在首帧完成时通过FlutterJNI
告知Embedder首帧数据已经绘制在Surface
上。
Engine
对于UI的绘制实际上是由Framework
驱动的,故以上逻辑其实不是线性的,即Embedder只负责Surface
的创建和通知,具体何时完成渲染取决于Engine
,而Engine
的渲染取决于Framework
,Framework
的启动其实又是由Embedder所驱动的。
总结
FlutterActivityAndFragmentDelegate
的onCreateView
()方法中主要做的工作:
- 初始化一个
FlutterSurfaceView
的实例, 并以此为参数创建FlutterView
(FrameLayout
的子类) - 注册一个
flutterUiDisplayListener
向Host
提供首帧渲染以及停止渲染的回调,首帧渲染完成的回调主要用于触发从闪屏到首帧的过渡动画 - 闪屏相关逻辑的初始化
attachToFlutterEngine
,将FlutterView
和FlutterEngine
绑定(包括RenderSurface
和FlutterRender
的绑定、各种平台能力、Platform View
的初始化)
下面开始分析Framework
的启动。
Framework 的启动
至此,Embedder已经完成FlutterEngine
和FlutterView
的初始化,Framework
的运行环境也已经准备完毕,可以开始执行Framework
的逻辑了,其触发时机位于FlutterActivity
的onStart
中,该方法最终将调用Delegate
的doInitialFlutterViewRun
方法,如代码清单4-19所示。
// 代码清单4-19 engine/shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java
private void doInitialFlutterViewRun() {
if (host.getCachedEngineId() != null) { return; } // 预加载的Engine无法通过本方法启动
if (flutterEngine.getDartExecutor().isExecutingDart()) { return; } // 已经在运行
String initialRoute = host.getInitialRoute(); // 获取初始路由
if (initialRoute == null) { // 8.7节将详细介绍Flutter的路由体系
initialRoute = maybeGetInitialRouteFromIntent(host.getActivity().getIntent());
if (initialRoute == null) { initialRoute = DEFAULT_INITIAL_ROUTE; } // 默认路由
flutterEngine.getNavigationChannel().setInitialRoute(initialRoute);
String appBundlePathOverride = host.getAppBundlePath();
if (appBundlePathOverride == null || appBundlePathOverride.isEmpty()) {
appBundlePathOverride = FlutterInjector.instance().flutterLoader().
findAppBundlePath();
} // 自定义assets资源地址,默认为flutter_assets
DartExecutor.DartEntrypoint entrypoint = new DartExecutor.DartEntrypoint(
appBundlePathOverride, host.getDartEntrypointFunctionName());
flutterEngine.getDartExecutor().executeDartEntrypoint(entrypoint);
}
以上逻辑中,首先会进行相关检查工作。appBundlePathOverride
字段用于向开发者提供一个自定义资源加载路径的接口,默认为flutter_assets
。以上逻辑最终会构造一个DartExecutor.DartEntrypoint
对象,并调用FlutterJNI
的nativeRunBundleAndSnapshotFromLibrary
方法完成Framework
的启动。
至此,Embedder 完整启动流程如下:
Engine入口整理
Embedder的启动流程,如图4-2所示。
在启动流程中,通过API回调或者JNI
主动调用,Embedder已经多处调用了Engine的逻辑,其中有 5 处逻辑对于 Engine 的初始化至关重要,整理如下。
-
JNI_OnLoad
:在System.loadLibrary
时触发,将完成Engine
接口和Embedder
接口的绑定注册。 -
nativeInit
:FlutterLoader
主动调用,在相关资源准备完成后调用,主要负责向Engine
传递必要的初始化参数。 -
nativeAttach
:Embedder的FlutterEngine
对象在完成必要的初始化后调用,将触发Engine中关键类的初始化逻辑。 -
nativeSurfaceCreated
:Java侧的Surface
对象(SurfaceView
)准备完成后调用,将触发Engine
中有关绘制的逻辑的初始化。 -
nativeRunBundleAndSnapshotFromLibrary
:FlutterEngine
和FlutterView
均初始化完成后,在Host
的onStart
生命周期中主动调用,用于触发Framework
的相关初始化工作。
Engine关键类及启动流程
在图4-3中,Engine
最底层的PlatformViewAndroidJNI
抽象了与Embedder
进行交互的接口,最上层的PlatformConfiguration
则抽象了与Framework
进行交互的接口。在Flutter中,基于Dart VM
的Framework
能够和基于Java VM
的Embedder
相互配合,正是得益于连接着PlatformViewAndroidJNI
和PlatformConfiguration
的这条“伟大航路”。例如Vsync
信号的注册与响应,以及Platform Channel
的运行,都是通过这条航路来完成Framework
和Embedder
的协作。
要彻底理解Engine
的运行原理,就要从两个角度理解图4-3所体现的类结构。
-
第1个角度是从Embedder出发,转发到Framework。在图4-3中,
Embedder
的入口是PlatformViewAndroidJNI
,它通过shell_holder
持有AndroidShellHolder
,AndroidShellHolder
又通过shell_
字段持有Shell
。Shell
是Engine
的枢纽,它可以通过platform_view_
字段转发逻辑到PlatformView
,可以通过rasterizer_
字段转发逻辑到Rasterizer
,可以通过engine_
字段转发逻辑到Engine
。Engine
是Flutter Framework
在Flutter Engine
中的抽象封装,Engine
通过runtime_controller_
字段持有RuntimeController
,顾名思义,RuntimeController
就是Flutter Framework
运行时的控制者。具体来说,RuntimeController
通过vm_
字段持有DartVM
,又通过root_isolate_
字段持有DartIsolate
,而后者的父类UIDartState
通过platform_configuration_
字段持有PlatformConfiguration
。PlatformConfiguration
是和Flutter Framework
直接进行交互的类,经过以上路径,一个Embedder
的逻辑可以转发到Framework
。 -
第2个角度是从Framework出发,转发到Embedder。这就涉及
Flutter Engine
中代理模式(主要是图4-3中Delegate
或者Client
结尾的类)的巧妙运用。具体来说,Flutter Framework
的逻辑将转发到PlatformConfiguration
,该类通过client_
字段持有PlatformConfigurationClient
,其正是RuntimeController
的父类,因此PlatformConfiguration
通过代理持有RuntimeController
,而RuntimeController
又通过RuntimeDelegate
持有Engine
,Engine
又通过Engine::Delegate
持有Shell
。Shell
是Flutter Engine
的枢纽,它通过platform_view_
字段持有PlatformView
,而PlatformView
的子类PlatformViewAndroid
又通过jni_facade_
字段持有PlatformViewAndroidJNI
, 该类正是和Embedder
进行交互的类。经过以上路径,一个Framework
逻辑可以转发到Embedder
。
事实上,以上只是UI
线程和Platform
线程的交互,Flutter Engine
可以通过Shell
完成UI
线程、Platform
线程、I/O
线程和Raster
线程的相互转发。
对Android平台而言,AndroidShellHolder
通常会创建和管理4个线程,分别是Platform
线程、UI
线程、Raster
线程和I/O
线程。
Platform
线程即Android
的主线程,Engine
不会在此线程执行特别的任务,主要用于和Embedder
的交互和通信,如Platform Channel
、Vsync
监听。UI
线程是Framework
逻辑所在的线程,UI
相关的操作(如3棵树的更新、动画等)都是在UI
线程中完成的。Raster
线程负责将UI
线程生成的数据做进一步处理并通过GPU
上屏,之所以设置单独一个线程是为了提升性能。I/O
线程负责处理一些繁重的、阻塞性的任务,如图片的解码、资源的存取。
总的来说,Platform
线程和UI
线程与开发者的关系最为密切,比如Platform Channel
在Embedder
中必须通过主线程(Platform
线程)发送,否则会失败。
需要注意的是,线程的创建和管理由Engine
的宿主(如AndroidShellHolder
)决定,对Engine
中的Engine
类而言,它所持有的只是4个TaskRunner
对象,至于这4个TaskRunner
对象是来自同一个线程还是4个独立的线程,Engine
类是无感知的。
Surface关键类及启动流程
Flutter绘制体系介绍
移动端绘制体系可以分为7层,如图4-5所示。
图4-5中,由上至下共分为7层。
- 第1层是应用层,即开发者编码实现的各种UI界面,供用户进行交互,即使在不同平台,用户仍然可以获得使用上几乎一致的体验。
- 第2层是框架层,是开发者进行UI开发的基本单位,比如Android中的View和Flutter中的Widget,框架层的特点是为开发者屏蔽了底层的绘制细节,提供了可复用的最小单元。
- 第3层是高级绘图接口(比如Skia),它们是框架层的基石,也可以基于高级绘图接口进行UI开发,但是需要处理的细节更多,框架层可以认为是高级绘图接口的语义化封装。
- 第4层是底层绘图接口,需要注意的是,这里的底层是相对Skia等高级绘图接口而言的。底层绘图接口提供了更接近图形学领域的API,还提供了绘制相关的最小单元的指令和属性。对底层绘图接口而言,最大的挑战就是需要处理不同硬件的兼容性问题,即不同厂商提供的硬件接口可能是不一样的,作为软件层,底层绘图接口需要处理。
- 第5层是桥接层,EGL的全称是Embedded Graphics Library,它是厂商提供的、符合统一规范的API,用以解决第4层在直接操作硬件接口时可能遇到的兼容性问题。
- 第6层是GPU,它比CPU拥有更多的计算单元和更少的存储单元,因而非常适合并发计算的场景。通常来说,通过CPU进行绘制被称为软绘,通过GPU进行绘制被称为硬绘,现代移动设备大多采用硬绘,因为硬绘的性能更佳。
- 第7层是渲染区,最典型的就是物理显示器,此外还有一些虚拟的渲染区,比如离屏Buffer、Virtual Display(Android)。应用层的UI实际要经过中间5层的处理,才能最终到达渲染区,完成UI的显示。
需要注意的是,以上分层并不是绝对的,即框架层也可能会直接调用底层绘图接口,底层绘图接口也可能直接操作渲染区,但以上分层是大多数UI框架会遵循和追求达到的架构。
对Flutter Engine来说,其绘制相关的关键类如图4-6所示。
图4-6中,PlatformViewAndroid
是Flutter Engine
中负责渲染的关键类,它持有两个关键对象,即通过android_context_
字段持有AndroidContext
,通过android_surface_
字段持有AndroidSurface
。
AndroidContext
提供了绘制相关的上下文,对于OpenGL
模式(Android平台默认的模式,下同),其具体子类为AndroidContextGL
。AndroidContextGL
通过EGLContext
、EGLConfig
等对象为Flutter Engine
的渲染提供上下文环境。
AndroidSurface
提供渲染的输出,是帧数据的消费者,对于OpenGL
模式,其具体子类为AndroidSurfaceGL
,该类通过onscreen_surface_
字段和offscreen_surface_
字段持有两个AndroidEGLSurface
对象。
AndroidEGLSurface
是Flutter Engine
对于EGL
API的封装,其内部持有EGLSurface
、EGLDisplay
、EGLContext
等关键实例,它们将负责真正的渲染逻辑。
此外,AndroidNativeWindow
是Embedder
中创建的Surface
在Engine
中的等价表示,AndroidSurfaceGL
通过native_window_
字段持有该类,用于onscreen_surface_
实例的创建。
上述类都位于Platform
线程,而Flutter Engine
的渲染实际发生在Raster
线程,为此,Raster
线程中的GPUSurfaceGL
将通过delegate_
字段持有一个GPUSurfaceGLDelegate
,其具体实现正是AndroidSurfaceGL
,如此,Raster
线程便可以操作AndroidEGLSurface
了。
在Flutter Engine
的Surface
的启动流程中,将多次调用EGL
API,虽然它们分散在各段代码中,但其调用顺序仍然会严格遵循图4-7所体现的流程规范。
图4-7展示了Flutter Engine中从Surface
准备到UI最终上屏所经历的7个关键EGL
API调用,具体作用已在图中注明。
Surface相关的初始化流程如下:
Dart Runtime 关键类及启动流程
Dart Runtime涉及的关键类及其关系如图4-9所示。
在图4-9中,Shell
是我们已经非常熟悉的类,它通过持有DartVMRef
对象,进而持有真正的Dart VM
。但从全局来看,真正持有Dart VM
的其实是RuntimeController
。
首先分析图4-9的右半部分,DartVM
在创建前会先执行DartVMData
的创建,该类包括两个关键字段——vm_snapshot_
和isolate_snapshot_
,这两个字段记录了vm-service Isolate
和主Isolate
进行启动所需要的Snapshot
数据(通过DartSnapshot
表示)。DartSnapshot
的data_
字段和instructions_
字段分别持有当前DartIsolate
启动所需要的数据序列和指令序列。DartSnapshot
的底层表示其实是Mapping
的子类,比如文件内存映射(FileMapping
,Debug
模式下),又或者动态链接库的符号映射(SymbolMapping
,Release
模式下)。
再分析图4-9的左半部分,RuntimeController
通过isolate_snapshot_
字段持有DartSnapshot
,进而可以基于该对象进行DartIsolate
的初始化,DartIsolate
继承自UIDartState
,是Engine
对于Dart_Isolate
(Dart VM
的API接口)的抽象表示。总的来说,Isolate
的启动就是加载并运行对应的Snapshot
(so
文件或者Kernel
文件)。