前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >浅析 FlutterBoost

浅析 FlutterBoost

作者头像
烧麦程
发布2022-12-05 14:13:50
2.8K0
发布2022-12-05 14:13:50
举报
文章被收录于专栏:半行代码半行代码

前言

在 native 和 Flutter 的混合开发中,混合栈是一个绕不开的话题。在 Flutter 2.x 版本 FlutterEngineGroup 的多引擎方案之前,直接使用 Flutter 多引擎的内存占用是比较大的。所以存在业界开源比较有名的单引擎方案 FlutterBoost。FlutterBoost 的地址是:https://github.com/alibaba/flutter_boost我们调用了它的方案后,顺便来看下它内部的工作流程和设计思路。

引擎复用

在 application 初始化的时候,会调 FlutterBoost 的 setup:

代码语言:javascript
复制
public void setup(Application application, FlutterBoostDelegate delegate, Callback callback, FlutterBoostSetupOptions options) {
  // 1. initialize default engine
 FlutterEngine engine = getEngine();
  if (engine == null) {
     if (options.flutterEngineProvider() != null) {
        FlutterEngineProvider provider = options.flutterEngineProvider();
        engine = provider.provideFlutterEngine(application);
      }
    
     if (engine == null) {
        engine = new FlutterEngine(application, options.shellArgs());
      }
     FlutterEngineCache.getInstance().put(ENGINE_ID, engine);
  }
  if (!engine.getDartExecutor().isExecutingDart()) {
  // Pre-warm the cached FlutterEngine.
  engine.getNavigationChannel().setInitialRoute(options.initialRoute());
  engine.getDartExecutor().executeDartEntrypoint(new DartExecutor.DartEntrypoint(
  FlutterMain.findAppBundlePath(), options.dartEntrypoint()));
 }
  // 2. set delegate
  getPlugin().setDelegate(delegate);
  //3. register ActivityLifecycleCallbacks
  setupActivityLifecycleCallback(application, isBackForegroundEventOverridden);
}

这里会创建复用的 Flutter 引擎。并且设置 delegate, 绑定生命周期的监听。最终 FlutterEngineCache 这个类会维护创建的 FlutterEngine对象。

FlutterActivity 里面会执行 delegate#onAttach 方法 这里的 flutterEngine 应该是复用唯一的,如果是null,说明是页面重建。就是去创建:

provideFlutterEngine 的逻辑由容器提供FlutterActivity默认没有提供FlutterFragment使用 Activity提供,如果Activity不是FlutterEngineProvider, 也返回nul View同理。如果没有提供,那么会创建一个FlutterEngine不过这里是有默认的引擎id的。也就是flutter_boost_default_engine`

代码语言:javascript
复制
FlutterEngineCache.getInstance().put(ENGINE_ID, engine);

getPlugin().setDelegate(delegate);
setupActivityLifecycleCallback(application, isBackForegroundEventOverridden);
代码语言:javascript
复制
void onAttach(@NonNull Context context) {
 if (flutterEngine == null) {
  setupFlutterEngine();
 }
}

混合栈路由

先看 Dart 侧的主要功能

dart

  1. 需要创建并初始化一个 Binding BoostFlutterBindingBoostFlutterBindingWidgetsFlutterBinding 的子类 实现了
  • initInstances
  • handleAppLifecycleStateChanged

主要是处理生命周期变化的。

  1. 创建app,里面放boost的组件 FlutterBoostApp ,传入路由表、initroute和拦截器 里面其实是 appBuilder 决定。在 initState 里会创建一个 BoostContainer。创建了 NativeRouterApiBoostFlutterRouterApi下一个frame完成的时候会执行:这个后面再看。
代码语言:javascript
复制
refreshOnPush(initialContainer);
_boostFlutterRouterApi.isEnvReady =true;
_addAppLifecycleStateEventListener();
BoostOperationQueue.instance.runPendingOperations();
  1. 开启新的页面

里面通过 FlutterBoostAppState 操作,在 instance 的时候初始化:

最后push是通过 FlutterBoostAppStatepushWithInterceptor 实现: 先忽略拦截器的逻辑,直接看不拦截的:

flutter页面和非flutter页面分别走 pushWithResultnativeROuterApi

如果withCotainer为true,则需要新的容器页,通过native去打开。如果withcontainer为false,则不需要新的容器。调用 pushPage这里触发 notifyListeners: 这里的监听反应是 setState, BoostContainerState 会刷新。flutterboost的层级为

HeroControllerScope下面的层级还包括了NavigatorExt ,这个是 flutterboost里自己封装的 Navigator 组件。页面监听则由BoostNavigatorObserver 负责:

Flutter内部跳转则根据 container.pages 来渲染页面:

如果 withContainer 是true:

则通过native去进行跳转:

这里通过dev.flutter.pigeon.NativeRouterApi.pushFlutterRoute 这个channel 进行通信 这里会打开新的 Flutter 容器页。接着看如果打开的路由不是一个 Flutter 页面:

调用 dev.flutter.pigeon.NativeRouterApi.pushNativeRoute 这个 channel

代码语言:javascript
复制

var result = topContainer.addPage(BoostPage.create(pageInfo));
// addPage
Future<T> addPage<T extends Object>(BoostPage page) {
 if (page != null) {
      _pages.add(page);
      notifyListeners();
      return page.popped;
    }
    return null;
}
  1. 关闭页面 使用 BoostNavigatorpop 关闭一个页面。这里根据页面是否能回退决定是退出一个页面还是退出整个容器:

退出容器调用dev.flutter.pigeon.NativeRouterApi.popRoute 这个 channel

Android

  1. 跳转flutter:用这个 Intent 去跳转。
代码语言:javascript
复制
Intent intent = new FlutterBoostActivity.CachedEngineIntentBuilder(
 FlutterBoostActivity.class
)
 .backgroundMode(FlutterActivityLaunchConfigs.BackgroundMode.opaque)
 .destroyEngineWithActivity(false)
 .url("flutterPage")
 .urlParams(params)
 .build(this);
  1. FlutterBoostActivity 继承了 FlutterActivity ,实现了 FlutterViewContainer 接口。onCreate 里面调用 添加Flutter容器,如果当前容器size为1,把flutter的生命周期改为 resumedonResume : 先尝试销毁前一个容器。在这里调用 performDetach 方法 接着调用 performAttach然后调用 FlutterPluginonContainerAppeared ,这里会打开flutter响应的路由
代码语言:javascript
复制
FlutterBoost.instance().getPlugin().onContainerCreated(this);
代码语言:javascript
复制
// FlutterBootPlugin onContainerCreated
FlutterContainerManager.instance()
 .addContainer(container.getUniqueId(), container);
if (FlutterContainerManager.instance().getContainerSize() == 1) {
 FlutterBoost.instance()
   .changeFlutterAppLifecycle(FlutterBoost.FLUTTER_APP_STATE_RESUMED);
}
代码语言:javascript
复制
public void onResume() {
 super.onResume();
 final FlutterContainerManager containerManager = FlutterContainerManager.instance();
 stage = LifecycleStage.ON_RESUME;
 FlutterViewContainer top = containerManager.getTopContainer();
 if (top != null && top != this) top.detachFromEngineIfNeeded();
 performAttach();
 textureHooker.onFlutterTextureViewRestoreState();
 FlutterBoost.instance().getPlugin().onContainerAppeared(this);
 getFlutterEngine().getLifecycleChannel().appIsResumed();
 platformPlugin.updateSystemUiOverlays();
}
  1. 处理跳转的 plugin部分dev.flutter.pigeon.NativeRouterApi.pushFlutterRoute这个是flutter使用新的容器跳转flutter页面的处理部分:

这里调用了 pushFlutterRoute 方法。具体实现在 FlutterBoostPlugin 里;这里最终逻辑由 FlutterBoostDelegatepushFlutterRoute 提供。boost的demo里如下实现:这里可以从api看出来容器是复用的。dev.flutter.pigeon.NativeRouterApi.pushNativeRoute处理打开新的native页面。和上面的类似,走FlutterBoostDelegatepushNativeRoute 方法。逻辑参考demo:如果是我们自己的代码,可以根据 FLutterBoostRouteOptions 里面的字段来判断我们需要跳转的页面。dev.flutter.pigeon.NativeRouterApi.popRoute退出容器页面。先调用 FlutterBoostDelegatepopRoute 方法:这里返回false表示不拦截。默认返回也是false。当退出此容器后,会调用销毁逻辑:finishContainer 由每个容器的实现类去实现:

都是把所在的 Activity 给 finish 掉。

代码语言:javascript
复制
@Override
public void pushFlutterRoute(FlutterBoostRouteOptions options) {
 Class<? extends FlutterBoostActivity> activityClass = options.opaque() ? FlutterBoostActivity.class : TransparencyPageActivity.class;
 Intent intent = new FlutterBoostActivity.CachedEngineIntentBuilder(activityClass)
  .destroyEngineWithActivity(false)
  // 注意:这里需要回传dart带过来的uniqueId,否则页面退出时传参可能失败。
  // 但,如果是从Native打开Flutter页面,请不要给uniqueId赋*任何值*!!!
  .uniqueId(options.uniqueId())
  .backgroundMode(options.opaque() ? BackgroundMode.opaque : BackgroundMode.transparent)
  .url(options.pageName())
  .urlParams(options.arguments())
  .build(FlutterBoost.instance().currentActivity());
 FlutterBoost.instance().currentActivity().startActivity(intent);
}
代码语言:javascript
复制
@Override
public void pushNativeRoute(FlutterBoostRouteOptions options) {
 Intent intent = new Intent(FlutterBoost.instance().currentActivity(), NativePageActivity.class);
 FlutterBoost.instance().currentActivity().startActivityForResult(intent, options.requestCode());
}
代码语言:javascript
复制
// demo
@Override
public boolean popRoute(FlutterBoostRouteOptions options) {
 Toast.makeText(FlutterBoost.instance().currentActivity(), "自定义popRoute处理逻辑", Toast.LENGTH_SHORT).show();
 return false;
}
代码语言:javascript
复制
// FlutterBoostPlugin popRoute
FlutterViewContainer container = FlutterContainerManager.instance()
 .findContainerById(uniqueId);
if (container != null) {
  container.finishContainer((Map<String, Object>) 
    (Object) params.getArguments());
}

结构

从 native 侧的代码里可以看到 FlutterBoost 的一些概念:

  • FlutterEngineCache 引擎管理类
  • FlutterBoost 主要逻辑的维护者
  • FlutterBoostPlugin 负责Flutter侧和native侧的通信
  • FlutterViewContainer Flutter页面承载

从 dart 侧可以总结出:

  • Container flutter容器
  • ContainerManager 容器管理类,负责跳转等操作

总结

这里就大概了解了 FlutterBoost 的工作流程。我们也可以总结一些优缺点:优点

  • 单引擎实现,内存占用比较低
  • 页面生命周期管理做的比较到位
  • 3.x 版本理论上和 Flutter 版本无关,不需要把 Flutter 版本和库版本绑定

但是 boost 的可见缺点也比较多

  • 接入相对复杂。但是3.x相对之前版本简单了很多。文档也比较齐全了
  • 侵入了 Flutter Android 侧的流程,native 层很多没有使用官方 ``io.flutter.embedding.android里的内容,后续Flutter` 升级后不一致的风险比较大。
  • github issus 还比较多。针对最新版本的 Flutter 适配的也比较慢,看起来其主要服务对象还是闲鱼内部。
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-09-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 半行代码 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 引擎复用
  • 混合栈路由
  • 结构
  • 总结
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档