背景
我们在 Flutter Android 端的 Java 层代码中经常看到 FlutterEngine、FlutterEngineGroup、FlutterEngineCache 等相关类的使用,你是不是也经常搞不清他们的关系和作用?本文就是对他们的一个解剖分析,由于 Flutter 2 版本对这块做了大调整,所以我们的分析以 2.2.3 版本为例分析。
FlutterEngine 相关分析
FlutterEngine 是一个独立的 Flutter 运行环境容器,通过它可以在 Android 应用程序中运行 Dart 代码。FlutterEngine 中的 Dart 代码可以在后台执行,也可以使用附带的 FlutterRenderer 和 Dart 代码将 Dart 端 UI 效果渲染到屏幕上,渲染可以开始和停止,从而允许 FlutterEngine 从 UI 交互转移到仅进行数据处理,然后又返回到 UI 交互的能力。
使用 FlutterEngine 执行 Dart 或 Flutter 代码需要先通过 FlutterEngine 获取 DartExecutor 引用,然后调用 DartExecutor 的executeDartEntrypoint(DartExecutor.DartEntrypoint)
执行 Dart 代码即可,同一个 FlutterEngine 实例中获取的 DartExecutor 的executeDartEntrypoint(DartExecutor.DartEntrypoint)
方法只能被调用一次,切记。
想要把 Flutter 内容渲染到屏幕上,需要调用 FlutterEngine 的getRenderer()
方法获取一个 FlutterRenderer 引用,然后让 FlutterRenderer 实例 attach 上一个 RenderSurface(譬如默认提供的 FlutterView,也即其内部的 FlutterSurfaceView、FlutterTextureView、FlutterImageView 之一,参见前面系列文章)。
App 每个进程中创建第一个 FlutterEngine 实例的时候会加载 Flutter 引擎的原生库并启动 Dart VM(VM 存活生命周期跟随进程),随后同进程中其他的 FlutterEngines 将在同一个 VM 实例上运行,但在运行 DartExecutor 时将拥有自己的 Dart Isolate。每个 Isolate 都是一个独立的 Dart 环境,除非通过 Isolate 端口,否则无法相互通信。[参见官方文档]
所以,对于一个多进程且多 FlutterEngine 的 app 来说,其 FlutterEngine 与 DartExecutor、Dart VM、Isolate 的关系大致如下图:
下面是 FlutterEngine 核心源码片段:
public class FlutterEngine {
//Flutter C/C++与平台java层接口定义交互。
@NonNull private final FlutterJNI flutterJNI;
//用来把Flutter Dart UI渲染到屏幕上,renderer会attach到RenderSurface上。
@NonNull private final FlutterRenderer renderer;
//Dart执行器。
@NonNull private final DartExecutor dartExecutor;
//用来管理安卓组件和Flutter plugins插件。
@NonNull private final FlutterEngineConnectionRegistry pluginRegistry;
//localization的安卓端实现插件。
@NonNull private final LocalizationPlugin localizationPlugin;
//一堆系统通道。
@NonNull private final AccessibilityChannel accessibilityChannel;
@NonNull private final DeferredComponentChannel deferredComponentChannel;
@NonNull private final KeyEventChannel keyEventChannel;
@NonNull private final LifecycleChannel lifecycleChannel;
@NonNull private final LocalizationChannel localizationChannel;
@NonNull private final MouseCursorChannel mouseCursorChannel;
@NonNull private final NavigationChannel navigationChannel;
@NonNull private final RestorationChannel restorationChannel;
@NonNull private final PlatformChannel platformChannel;
@NonNull private final SettingsChannel settingsChannel;
@NonNull private final SystemChannel systemChannel;
@NonNull private final TextInputChannel textInputChannel;
// Platform Views.
@NonNull private final PlatformViewsController platformViewsController;
// Engine Lifecycle.
@NonNull private final Set<EngineLifecycleListener> engineLifecycleListeners = new HashSet<>();
//......
//全参数的构造函数,各种构造最终都走进这里
public FlutterEngine(
@NonNull Context context,
@Nullable FlutterLoader flutterLoader,
@NonNull FlutterJNI flutterJNI,
@NonNull PlatformViewsController platformViewsController,
@Nullable String[] dartVmArgs,
boolean automaticallyRegisterPlugins,
boolean waitForRestorationData) {
//......
//创建一个DartExecutor并将flutterJNI和安卓平台的assetManager实例传递进去。
this.dartExecutor = new DartExecutor(flutterJNI, assetManager);
this.dartExecutor.onAttachedToJNI();
//......
//各种channel实例化,下面独立分析。
accessibilityChannel = new AccessibilityChannel(dartExecutor, flutterJNI);
deferredComponentChannel = new DeferredComponentChannel(dartExecutor);
keyEventChannel = new KeyEventChannel(dartExecutor);
lifecycleChannel = new LifecycleChannel(dartExecutor);
localizationChannel = new LocalizationChannel(dartExecutor);
mouseCursorChannel = new MouseCursorChannel(dartExecutor);
navigationChannel = new NavigationChannel(dartExecutor);
platformChannel = new PlatformChannel(dartExecutor);
restorationChannel = new RestorationChannel(dartExecutor, waitForRestorationData);
settingsChannel = new SettingsChannel(dartExecutor);
systemChannel = new SystemChannel(dartExecutor);
textInputChannel = new TextInputChannel(dartExecutor);
//......
//插件实例化。
this.localizationPlugin = new LocalizationPlugin(context, localizationChannel);
this.flutterJNI = flutterJNI;
if (flutterLoader == null) {
flutterLoader = FlutterInjector.instance().flutterLoader();
}
//......
this.pluginRegistry =
new FlutterEngineConnectionRegistry(context.getApplicationContext(), this, flutterLoader);
//默认就是自动注册plugins的,可以通过清单文件配置变更等。
if (automaticallyRegisterPlugins && flutterLoader.automaticallyRegisterPlugins()) {
registerPlugins();
}
}
//......
//注册flutter项目根目录下pubspec.yaml中依赖的所有flutter plugins。
//Flutter tool会生成一个GeneratedPluginRegistrant的类。
private void registerPlugins() {
try {
Class<?> generatedPluginRegistrant = Class.forName("io.flutter.plugins.GeneratedPluginRegistrant");
Method registrationMethod = generatedPluginRegistrant.getDeclaredMethod("registerWith", FlutterEngine.class);
registrationMethod.invoke(null, this);
} catch (Exception e) {
Log.w(TAG, "Tried to automatically register plugins with FlutterEngine ("
+ this + ") but could not find and invoke the GeneratedPluginRegistrant.");
}
}
//......省略一堆属性成员的get方法
}
上面代码片段其实挺直观,主要就是负责各种插件、Channel、渲染环境、运行环境的实例化准备工作。我们重点看一下上面的registerPlugins()
方法,他内部反射调用了io.flutter.plugins.GeneratedPluginRegistrant
类的registerWith(this)
方法把当前 FlutterEngine 实例传递进去。
你可能会问,这个io.flutter.plugins.GeneratedPluginRegistrant
类是哪里来的呢?其实在 Flutter 项目根目录下pubspec.yaml
文件的依赖中如果有 Flutter Plugin 则会在执行flutter pub get
等 Flutter tools 命令时自动生成一个名为 GeneratedPluginRegistrant 的类,其中包含依赖的 Flutter Plugin 相关 add 代码。我们以一个 demo 为例来进行说明,如下图示在pubspec.yaml
中追加了 webview_flutter 依赖,本质是一个 Flutter Plugin,运行 pub get 后的效果如下:
可以看到,在构造实例化 FlutterEngine 时会调用其registerPlugins()
方法,registerPlugins()
方法会反射调用自动生成的io.flutter.plugins.GeneratedPluginRegistrant
类的registerWith(this)
方法把当前 FlutterEngine 实例传递进去。而io.flutter.plugins.GeneratedPluginRegistrant
类的registerWith(this)
方法中主要就是将我们在pubspec.yaml
文件中的 Flutter Plugin 依赖追加到 Plugins 集合中。
我们先看下flutterEngine.getPlugins().add(xxx)
方法:
class FlutterEngineConnectionRegistry
implements PluginRegistry,
ActivityControlSurface,
ServiceControlSurface,
BroadcastReceiverControlSurface,
ContentProviderControlSurface {
//......
@Override
public void add(@NonNull FlutterPlugin plugin) {
//......
plugins.put(plugin.getClass(), plugin);
plugin.onAttachedToEngine(pluginBinding);
// For ActivityAware plugins, add the plugin to our set of ActivityAware
// plugins, and if this engine is currently attached to an Activity,
// notify the ActivityAware plugin that it is now attached to an Activity.
if (plugin instanceof ActivityAware) {
ActivityAware activityAware = (ActivityAware) plugin;
activityAwarePlugins.put(plugin.getClass(), activityAware);
if (isAttachedToActivity()) {
activityAware.onAttachedToActivity(activityPluginBinding);
}
}
// For ServiceAware plugins, add the plugin to our set of ServiceAware
// plugins, and if this engine is currently attached to a Service,
// notify the ServiceAware plugin that it is now attached to a Service.
if (plugin instanceof ServiceAware) {
ServiceAware serviceAware = (ServiceAware) plugin;
serviceAwarePlugins.put(plugin.getClass(), serviceAware);
if (isAttachedToService()) {
serviceAware.onAttachedToService(servicePluginBinding);
}
}
// For BroadcastReceiverAware plugins, add the plugin to our set of BroadcastReceiverAware
// plugins, and if this engine is currently attached to a BroadcastReceiver,
// notify the BroadcastReceiverAware plugin that it is now attached to a BroadcastReceiver.
if (plugin instanceof BroadcastReceiverAware) {
BroadcastReceiverAware broadcastReceiverAware = (BroadcastReceiverAware) plugin;
broadcastReceiverAwarePlugins.put(plugin.getClass(), broadcastReceiverAware);
if (isAttachedToBroadcastReceiver()) {
broadcastReceiverAware.onAttachedToBroadcastReceiver(broadcastReceiverPluginBinding);
}
}
// For ContentProviderAware plugins, add the plugin to our set of ContentProviderAware
// plugins, and if this engine is currently attached to a ContentProvider,
// notify the ContentProviderAware plugin that it is now attached to a ContentProvider.
if (plugin instanceof ContentProviderAware) {
ContentProviderAware contentProviderAware = (ContentProviderAware) plugin;
contentProviderAwarePlugins.put(plugin.getClass(), contentProviderAware);
if (isAttachedToContentProvider()) {
contentProviderAware.onAttachedToContentProvider(contentProviderPluginBinding);
}
}
}
//......
}
可以看到,FlutterEngineConnectionRegistry 的 add 方法不需要我们做过多解释就能看懂,主要就是添加一个 FlutterPlugin 实例,然后调用 FlutterPlugin 接口约定的一堆类似生命周期方法,譬如 onAttachedToEngine,然后依据插件的具体类型(安卓平台组件类型,Activity、Service、Broadcast、ContentProvider)进行对应的方法调用,这样 Flutter Plugin 插件开发者就能依据这些时机方法进行自己的平台逻辑处理。譬如上面 demo 中 webview_flutter Flutter Plugin 源码中的实现,如下:
public class WebViewFlutterPlugin implements FlutterPlugin {
private FlutterCookieManager flutterCookieManager;
//......
//FlutterEngineConnectionRegistry的add中触发调用,实例化BinaryMessenger和FlutterCookieManager。
@Override
public void onAttachedToEngine(FlutterPluginBinding binding) {
BinaryMessenger messenger = binding.getBinaryMessenger();
binding.getPlatformViewRegistry().registerViewFactory("plugins.flutter.io/webview", new WebViewFactory(messenger, /*containerView=*/ null));
flutterCookieManager = new FlutterCookieManager(messenger);
}
//FlutterEngineConnectionRegistry的remove中触发调用,移除插件。
@Override
public void onDetachedFromEngine(FlutterPluginBinding binding) {
if (flutterCookieManager == null) {
return;
}
flutterCookieManager.dispose();
flutterCookieManager = null;
}
}
这下搞懂每次编译 Flutter Android App 时自动生成的 GeneratedPluginRegistrant 是咋回事了吧,也知道为啥要求 GeneratedPluginRegistrant 类需要在混淆清单中被 keep 住的原因了吧。整体流程大致如下图:
关于 FlutterEngine 构造函数中的各种实例化 Channel 我们这里先不展开,后面单独篇章解析。
FlutterEngineCache 相关分析
FlutterEngineCache 其实很简单,目的就是一个进程单例模式,其中通过 Map 存储缓存 FlutterEngine 实例,代码也没啥好分析的。
public class FlutterEngineCache {
private static FlutterEngineCache instance;
//单例模式
public static FlutterEngineCache getInstance() {
if (instance == null) {
instance = new FlutterEngineCache();
}
return instance;
}
//基于key缓存FlutterEngine实例集合
private final Map<String, FlutterEngine> cachedEngines = new HashMap<>();
//判断是否包含指定id的FlutterEngine实例
public boolean contains(@NonNull String engineId) {
return cachedEngines.containsKey(engineId);
}
//获取指定id的FlutterEngine实例
public FlutterEngine get(@NonNull String engineId) {
return cachedEngines.get(engineId);
}
//缓存指定id的FlutterEngine实例
public void put(@NonNull String engineId, @Nullable FlutterEngine engine) {
if (engine != null) {
cachedEngines.put(engineId, engine);
} else {
cachedEngines.remove(engineId);
}
}
//删除指定id的FlutterEngine实例
public void remove(@NonNull String engineId) {
put(engineId, null);
}
//清空整个cache
public void clear() {
cachedEngines.clear();
}
}
FlutterActivity 支持和缓存的 FlutterEngine 一起使用,可以通过 FlutterActivity.withCachedEngine(String) 构建一个 FlutterActivity Intent,该 Intent 配置为使用现有的缓存 FlutterEngine。使用缓存的 FlutterEngine 时,该 FlutterEngine 应当已经执行 Dart 代码,也就是说 Dart 入口点和初始路由已经定义,所以 CachedEngineIntentBuilder 不提供这些配置特性。推荐使用 FlutterEngineCache,这样做可以预热引擎,减少启动 Flutter 页面时的白屏或者等待时间,就像官方说的一样。
FlutterEngineGroup 相关分析
比较早接触 Flutter 的小伙伴应该都知道,Flutter 混合开发中,多个 Flutter 页面(FlutterActivity)模式最被诟病的问题之一就是会生成多个 FlutterEngine 实例且每个 FlutterEngine 实例非常占用内存,所以才有了民间类似咸鱼 Flutter 的 Flutter Boost 方案,采用单 FlutterEngine 方案(分屏等场景无法兼容)且整个单进程 App 在同一个 Isolate 下做到内存共享。后来由于社区呼声太高,官方也努力在 Flutter 2.0 发布了实验特性的 FlutterEngineGroup 多 FlutterEngine 官方解决方案,即每个页面是一个 FlutterEngine,或者一个页面内包含多个 FlutterEngine,每个 FlutterEngine 对应一个 Isolate 且内存不共享。官方说从 FlutterEngineGroup 生成的 FlutterEngine 内存只增加 180k,因为它对常用资源进行共享(例如 GPU 上下文、字体度量和隔离线程的快照等),加快首次渲染的速度、降低延迟并降低内存占用。但是到目前 Flutter 2.2 版本为止,FlutterEngineGroup 依旧处于实验特性阶段,不推荐在正式项目中使用,参见官方 multiple-flutters 文档。下面先看下核心源码:
public class FlutterEngineGroup {
final List<FlutterEngine> activeEngines = new ArrayList<>();
//......
//通过FlutterEngineGroup实例创建一个FlutterEngine,第一个FlutterEngine和普通创建没区别,之后的就有区别了。
public FlutterEngine createAndRunEngine(
@NonNull Context context, @Nullable DartEntrypoint dartEntrypoint) {
FlutterEngine engine = null;
//......
if (activeEngines.size() == 0) {
//来自FlutterEngineGroup创建的第一个FlutterEngine,和普通创建没区别。
engine = createEngine(context);
engine.getDartExecutor().executeDartEntrypoint(dartEntrypoint);
} else {
//来自FlutterEngineGroup创建的非第一个FlutterEngine,基于第一个进行spawn操作返回。
engine = activeEngines.get(0).spawn(context, dartEntrypoint);
}
activeEngines.add(engine);
//......
return engine;
}
FlutterEngine createEngine(Context context) {
return new FlutterEngine(context);
}
}
可以看到,与普通 FlutterEngine 的区别在于 FlutterEngineGroup 的 createAndRunEngine 方法创建会有不同,具体在于,createAndRunEngine 方法创建第一个 FlutterEngine 实例与普通无区别,当创建第二个时会通过第一个 FlutterEngine 实例的spawn(context, dartEntrypoint)
方法进行创建,所以我们去看下 FlutterEngine 的 spawn 方法,如下:
FlutterEngine spawn(@NonNull Context context, @NonNull DartEntrypoint dartEntrypoint) {
//......
FlutterJNI newFlutterJNI =
flutterJNI.spawn(
dartEntrypoint.dartEntrypointFunctionName, dartEntrypoint.dartEntrypointLibrary);
return new FlutterEngine(
context, // Context.
null, // FlutterLoader. A null value passed here causes the constructor to get it from the
// FlutterInjector.
newFlutterJNI); // FlutterJNI.
}
很明显看到 spawn 方法是 FlutterEngine 的 c/c++ 层实现的,我们不再跟进,可以通过他的注释知道,这种基于当前 FlutterEngine 创建第二个 FlutterEngine 的方式会通过共享尽可能多的资源以最小化启动延迟和内存成本,这也就是官方的解决方案。具体样例 demo 参见官方 github 中 samples 的 add_to_app/multiple_flutters。
总结
可以看到,FlutterEngine、FlutterEngineCache、FlutterEngineGroup 的本质都是 FlutterEngine,多出的只是空间换时间,共用换空间的机制,其他官方 FlutterEngineGroup 早日稳定正式版。