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
&#