Flutter Android 端 FlutterEngine Java 相關流程源碼分析

工匠若水 2021-08-15 18:16:53 阅读数:1,001

本文一共[544]字,预计阅读时长:1分钟~
flutter android flutterengine java 流程

這是我參與8月更文挑戰的第4天,活動詳情查看: 8月更文挑戰

Flutter 系列文章連載~

背景

我們在 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 早日穩定正式版。

版权声明:本文为[工匠若水]所创,转载请带上原文链接,感谢。 https://gsmany.com/2021/08/20210815181419036y.html