本文將以 Android 視角,來扒一扒 Flutter 混合棧的前世此生。其實也就是從 1.0 正式發佈到如今 1.9 版本的一些變動。java
本文將會從如下幾個方面來分析:android
一個新的技術的興起,必然是一步一步向前的,Flutter 在成熟的 Android、iOS 的大環境下的生存,必要與 native 融合。git
一些成熟的 APP ,若是想使用 Flutter 技術,必然不會徹底使用 Flutter 重寫,那樣的成本過高,因此 Native + Flutter 的項目就出現了,Native 頁面與 Flutter 頁面共存,甚至交替呈如今用戶手機上。github
那什麼是混合棧呢?bootstrap
相信你已經有了答案,下面咱們用 Android 的視角來從新審視一下混合棧。緩存
咱們使用 flutter boost 框架提供的 demo 來看一下混合棧的效果(debug包)cookie
知道了混合棧問題,接下來咱們要考慮爲何會出現這樣的問題。session
現狀是一個 Activity 中存在多個 Flutter Page,咱們想要的是 Flutter Page 與 Android Activity 能夠一一對應,這樣即可以簡單的將兩個頁面棧合併成一個棧,而且在性能上不會產生影響,這就是咱們處理混合棧的目標。架構
如上圖混合棧所示,Android 中以 FlutterView 承載 Flutter 的展現,默認狀況下不一樣 FlutterView 建立了不一樣的 engine。若是一個 Flutter Page 對應一個 Activity, 這就致使了資源的屢次重複建立和內存的不共享。app
若是極端狀況下,Native => Flutter => Native => … => Native => Flutter 會是什麼狀況呢?後果不堪設想,固然,能夠從業務上避免這樣的問題,可是做爲框架的制定者,必需要考慮到這樣的問題。
接下來咱們看如何解決問題。須要從原理入手,須要閱讀源碼
從最經典的 Flutter 架構圖入手
從圖中能夠看到 3 層架構,每層提供了不一樣的能力
Flutter 從加載到顯示
mp.weixin.qq.com/s/ncViI0KGi…
The Engine architecture
github.com/flutter/flu…
Custom Flutter Engine Embedders
github.com/flutter/flu…
咱們看一下建立 Flutter 項目中自動生成的 Android 工程,內部使用的是 io.flutter.app 包內的 FlutterActivity,暫且不討論 io.flutter.embedding.android 包相關內容,後面會分析。
在 Android 中使用 Flutter 是這樣的
如上圖所示,圖中羅列了一些類,這裏講解一下
其實 Flutter 的運行機制就是這樣的,Flutter 由 FlutterView 呈現。每個 FlutterView,會對應的建立一個 FlutterNativeView,建立一個 Dart VM。而不一樣 FlutterView 內部的 Dart 代碼內存沒法共享。
建議讀者閱讀一下代碼
io.flutter.app.FlutterActivit
Io.flutter.app.FlutterActivityDelegate
io.flutter.view.FlutterMain
io.flutter.view.FlutterView
io.flutter.view.FlutterNativeView
io.flutter.embedding.engine.dart.DartExecutor
io.flutter.embedding.engine.FlutterJNI
經過上面的介紹,應該依然瞭解 Flutter 在 Android 上的運行機制,若是閱讀了源碼應該有更深的印象。在這樣的運行機制中,咱們能作什麼呢?其實 Flutter Boost 框架給了咱們解決的思路,可是這裏我仍是但願讀者能本身來想一想,若是是你本身來實現,該怎麼作呢?
來看看社區爲咱們提供的方案吧。
網上的文章不少,在文章最後提供一些連接,有興趣的讀者能夠都看一下。這裏僅以 Flutter Boost 爲例,來分析一下。
Flutter Boost 的處理方案能夠分紅兩個版本,使用了兩種方案,能夠做爲混合棧方案的兩種表明思路。
alibaba/flutter_boost 0.0.420
github.com/alibaba/flu…
FlutterView 複用方案
框架中從 FlutterActivityDelegate#onCreate 方法入手,重寫建立 FlutterView 的流程,複用 FlutterView 來實現。
咱們先來看一下這個版本的接入方式,在 Application 中初始化
FlutterBoostPlugin.init(new IPlatform() {
...
/** * 獲取應用入口的Activity,這個Activity在應用交互期間應該是一直在棧底的 * 提供給框架內部,後續建立FlutterView用 */
@Override
public Activity getMainActivity() {
if (MainActivity.sRef != null) {
return MainActivity.sRef.get();
}
return null;
}
...
});
複製代碼
咱們來看一下 FlutterActivityDelegate#onCreate 是如何處理的。
# FlutterActivityDelegate.java
@Override
public void onCreate(Bundle savedInstanceState) {
...
// 獲取 flutterView
flutterView = viewFactory.createFlutterView(activity);
// 爲空則建立,不然直接使用
if (flutterView == null) {
// 建立 FlutterView 的同時,建立 FlutterNative、DartExecutor
FlutterNativeView nativeView = viewFactory.createFlutterNativeView();
flutterView = new FlutterView(activity, null, nativeView);
flutterView.setLayoutParams(matchParent);
activity.setContentView(flutterView);
launchView = createLaunchView();
if (launchView != null) {
addLaunchView();
}
}
...
}
複製代碼
createFlutterView 的實現是在 FlutterActivity 中的。
# FlutterActivity.java
@Override
public FlutterView createFlutterView(Context context) {
return null;
}
複製代碼
而 Flutter Boost 框架重寫了 createFlutterView 方法
# BoostFlutterActivity.java
public FlutterView createFlutterView(Context context) {
return FlutterBoostPlugin.viewProvider().createFlutterView(this);
}
複製代碼
真正返回的是這裏構造的
# FlutterViewProvider.java
@Override
public BoostFlutterView createFlutterView(IFlutterViewContainer container) {
// 在 Application 中提供的 mPlatform,將緩存的 Activity 提供給這裏
Activity activity = mPlatform.getMainActivity();
if(activity == null) {
Debuger.log("create Flutter View not with MainActivity");
activity = container.getActivity();
}
// 若是爲 null 則建立而後緩存,有值則直接使用
if (mFlutterView == null) {
// BoostFlutterView 繼承自 FlutterView
mFlutterView = new BoostFlutterView(activity, null, createFlutterNativeView(container));
}
return mFlutterView;
}
複製代碼
這樣就複用了 FlutterView。
複用 FlutterView 須要在 Activity 切換的時候進行 view 的 attach、detach,該版本使用了截圖方案。
FlutterEngine 複用
從接入方式來看,在 Application 中初始化,提供了一個回調方法,提供 BoostFlutterEngine 便是 FlutterEngine 實例。
FlutterBoost.init(new Platform() {
...
@Override
public IFlutterEngineProvider engineProvider() {
return new BoostEngineProvider() {
@Override
public BoostFlutterEngine createEngine(Context context) {
return new BoostFlutterEngine(context, new DartExecutor.DartEntrypoint(
context.getResources().getAssets(),
FlutterMain.findAppBundlePath(context),
"main"), "/");
}
};
}
...
});
複製代碼
在看 BoostFlutterActivity 實現
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
mFlutterEngine = createFlutterEngine();
mFlutterView = createFlutterView(mFlutterEngine);
setContentView(mFlutterView);
...
}
...
protected BoostFlutterEngine createFlutterEngine(){
return FlutterBoost.singleton().engineProvider().provideEngine(this);
}
複製代碼
真正返回的是這裏構造的
# com.idlefish.flutterboost.BoostEngineProvider
@Override
public BoostFlutterEngine provideEngine(Context context) {
Utils.assertCallOnMainThread();
if (mEngine == null) {
FlutterShellArgs flutterShellArgs = new FlutterShellArgs(new String[0]);
FlutterMain.ensureInitializationComplete(
context.getApplicationContext(), flutterShellArgs.toArray());
// 這裏調用的方法就是初始化時重寫內容
mEngine = createEngine(context.getApplicationContext());
final IStateListener stateListener = FlutterBoost.sInstance.mStateListener;
if(stateListener != null) {
stateListener.onEngineCreated(mEngine);
}
}
return mEngine;
}
複製代碼
該版本的處理方式與 io.flutter.embedding 包中處理方式基本相同,使用了 flutter.jar 中的 FlutterSurfaceView 和 FlutterTextureView 來最終的展現 Flutter 界面。接下來咱們看看 Flutter 官方爲咱們提供的方式。
Experimental: Adding Flutter to Android
github.com/flutter/flu…
經過文檔咱們就能夠知道,該方式和 Flutter 項目自動生成的 Android 工程不一樣,使用的大多爲 io.flutter.embedding 包中的內容,而且提供了使用緩存的 FlutterEngine 的方式。使用了 FlutterEngineCache 類進行對 FlutterEngine 的 key-value 緩存。
在 flutter.jar 中能夠看到,共存了兩個 FlutterActivity、FlutterView 等。
FlutterActivity
io.flutter.app.FlutterActivity
io.flutter.embedding.FlutterActivity
FlutterView
io.flutter.view.FlutterView
io.flutter.embedding.FlutterView
這裏簡單介紹一下
# io.flutter.embedding.FlutterActivity.java
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
delegate = new FlutterActivityAndFragmentDelegate(this);
// 建立 Flutter
// 提供 FlutterEngine 並綁定到 Activity、建立並配置 PlatformPlugin、
delegate.onAttach(this);
...
// 建立 Flutter 的 View 並綁定到 Activity
setContentView(createFlutterView());
...
}
@NonNull
private View createFlutterView() {
return delegate.onCreateView(
null /* inflater */,
null /* container */,
null /* savedInstanceState */);
}
複製代碼
加載到 Activity 上的 View 是如何建立的
# io.flutter.embedding.android.FlutterActivityAndFragmentDelegate.java
@NonNull
View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
...
// 建立了一個 FlutterView
flutterView = new FlutterView(host.getActivity(), host.getRenderMode(), host.getTransparencyMode());
// 建立了一個 FlutterSplashView,包裹了一個 FlutterView 的View
flutterSplashView = new FlutterSplashView(host.getContext());
...
// 在 FlutterView 展現第一幀以前,先展現提供的 splashScreen
flutterSplashView.displayFlutterViewWithSplash(flutterView, host.provideSplashScreen());
return flutterSplashView;
}
複製代碼
看一下 FlutterView 是如何實現的
private void init() {
// 根據 renderMode 模式來選擇使用 SurfaceView/TextureView,解決了 SurfaceView 對動畫支持差的詬病
switch (renderMode) {
case surface:
FlutterSurfaceView flutterSurfaceView = new FlutterSurfaceView(getContext(), transparencyMode == TransparencyMode.transparent);
renderSurface = flutterSurfaceView;
addView(flutterSurfaceView);
break;
case texture:
FlutterTextureView flutterTextureView = new FlutterTextureView(getContext());
renderSurface = flutterTextureView;
addView(flutterTextureView);
break;
}
setFocusable(true);
setFocusableInTouchMode(true);
}
複製代碼
大體的流程就是這樣的。
Flutter 的混合棧處理在這一年多內,也有了很大的發展,從整個發展歷程來梳理,可能對混合棧的方案有更深的理解。最後附上整理的混合棧相關文章
Flutter 混合棧處理相關文章:
碼上用它開始 Flutter 混合開發——FlutterBoos
my.oschina.net/yunqi/blog/…Flutter 新銳專家之路:混合開發片
juejin.im/post/5b764a…微店的 Flutter 混合棧管理技術實踐
juejin.im/post/5c419c…Flutter 實現原理及在馬蜂窩的跨平臺開發事件
juejin.im/post/5d37b3…讓 Flutter 真正支持 View級別的混合開發
mp.weixin.qq.com/s?__biz=MzI…