本文将以 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…