Flutter:浅析flutter-boost原理

简介: Android之前我们了解了native和flutter的几种交互,有了这个知识就很容易理解flutter-boost原理。那么它是怎么实现的?

Android


之前我们了解了native和flutter的几种交互,有了这个知识就很容易理解flutter-boost原理。那么它是怎么实现的?


flutter-boost自定义了一个Activity —— BoostFlutterActivity,使用的时候会通过NewEngineIntentBuilder创建一个Intent,它的build代码:


public Intent build(@NonNull Context context) {
    ...
    return new Intent(context, activityClass)
            .putExtra(EXTRA_BACKGROUND_MODE, backgroundMode)
            .putExtra(EXTRA_DESTROY_ENGINE_WITH_ACTIVITY, false)
            .putExtra(EXTRA_URL, url)
            .putExtra(EXTRA_PARAMS, serializableMap);
}
复制代码


可以看到,它不仅仅支持route,同时还支持传参params,这正是我们需要的,那么这是怎么实现的?

首先看它的onCreate函数:


@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    ...
    delegate = new FlutterActivityAndFragmentDelegate(this);
    delegate.onAttach(this);
    ...
    setContentView(createFlutterView());
    ...
}
复制代码


通过createFlutterView创建了一个view,并setContentView。在createFlutterView中:


protected View createFlutterView() {
    return delegate.onCreateView(null,null,null);
}
复制代码


delegate是FlutterActivityAndFragmentDelegate对象,它的onCreateView:


public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    mSyncer = FlutterBoost.instance().containerManager().generateSyncer(this);
    ensureAlive();
    flutterView = new XFlutterView(host.getActivity(), FlutterBoost.instance().platform().renderMode(), host.getTransparencyMode());
    ...
    mSyncer.onCreate();
    return flutterSplashView;
}
复制代码


第一行通过containerManager().generateSyncer创建了一个mSyncer,containerManager()得到的是一个FlutterViewContainerManager对象,它的generateSyncer:


@Override
public IOperateSyncer generateSyncer(IFlutterViewContainer container) {
    ...
    ContainerRecord record = new ContainerRecord(this, container);
    ...
    mRefs.add(new ContainerRef(record.uniqueId(),container));
    return record;
}
复制代码


这里可以看到mSyncer实际上是一个ContainerRecord。这个很重要,后面会通过它实现route。

但是目前还没看到url和params是如何被使用的,那么回头看BoostFlutterActivity的onResume函数,BoostFlutterActivity所有生命周期函数都会调用delegate对应的函数,所以直接看它的onResume:


public void onResume() {
    mSyncer.onAppear();
    ...
}
复制代码


可以看到一开始就调用了mSyncer的onAppear函数,而mSyncer前面已经知道是ContainerRecord,那么它的onAppear:


@Override
public void onAppear() {
    ...
    mState = STATE_APPEAR;
    mManager.pushRecord(this);
    mProxy.appear();
    mContainer.getBoostFlutterView().onAttach();
}
复制代码


重点是mProxy.appear(),这个mProxy是内部类MethodChannelProxy,它的appear:


private void appear() {
    invokeChannelUnsafe("didShowPageContainer",
            mContainer.getContainerUrl(),
            mContainer.getContainerUrlParams(),
            mUniqueId
    );
    mState = STATE_APPEAR;
}
复制代码


这里我们看到了mContainer.getContainerUrl()mContainer.getContainerUrlParams(),这个mContainer就是最开始FlutterBoost.instance().containerManager().generateSyncer(this);传入的this,就是FlutterActivityAndFragmentDelegate,它的这两个函数(Host接口的)则调用了BoostFlutterActivity(实现了Host)对应的函数:


@Override
public String getContainerUrl() {
    if (getIntent().hasExtra(EXTRA_URL)) {
        return getIntent().getStringExtra(EXTRA_URL);
    }
    return "";
}
@Override
public Map getContainerUrlParams() {
    if (getIntent().hasExtra(EXTRA_PARAMS)) {
        SerializableMap serializableMap = (SerializableMap) getIntent().getSerializableExtra(EXTRA_PARAMS);
        return serializableMap.getMap();
    }
    Map<String, String> params = new HashMap<>();
    return params;
}
复制代码


这就是我们传入的url和params。所以接下来就来看invokeChannelUnsafe函数是怎么执行的:


public void invokeChannelUnsafe(String method, String url, Map params, String uniqueId) {
    HashMap<String, Object> args = new HashMap<>();
    args.put("pageName", url);
    args.put("params", params);
    args.put("uniqueId", uniqueId);
    FlutterBoost.instance().channel().invokeMethodUnsafe(method, args);
}
复制代码


FlutterBoost的channel()返回的是FlutterBoostPlugin,它的invokeMethodUnsafe层层调用最终执行:


public void invokeMethod(final String name, Serializable args, MethodChannel.Result result) {
    ...
    mMethodChannel.invokeMethod(name, args, result);
}
复制代码


mMethodChannel是MethodChannel类型,这个我们之前重点讲解了,是native和flutter的交互方式之一。所以最终就是执行了flutter的didShowPageContainer,并将url和params作为参数传入。那么flutter中如何处理的?经过搜索发现是在ContainerCoordinator类中:


Future<dynamic> _onMethodCall(MethodCall call) {
    final String pageName = call.arguments['pageName'] as String;
    final Map<String, dynamic> params =
        (call.arguments['params'] as Map<dynamic, dynamic>)
            ?.cast<String, dynamic>();
    final String uniqueId = call.arguments['uniqueId'] as String;
    switch (call.method) {
      ...
      case 'didShowPageContainer':
        nativeContainerDidShow(pageName, params, uniqueId);
        break;
      ...
    }
    return Future<dynamic>(() {});
  }
  bool nativeContainerDidShow(
  String name,
  Map<String, dynamic> params,
  String pageId,
) {
  FlutterBoost.containerManager
      ?.showContainer(_createContainerSettings(name, params, pageId));
  // Compatible to accessibility mode on Android.
  if (Platform.isAndroid) {
    try {
      final SemanticsOwner owner =
          WidgetsBinding.instance.pipelineOwner?.semanticsOwner;
      final SemanticsNode root = owner?.rootSemanticsNode;
      root?.detach();
      root?.attach(owner);
    } catch (e) {
      assert(false, e.toString());
    }
  }
复制代码


didShowPageContainer对应的执行方法是nativeContainerDidShow,它的第一行代码执行了containerManager.showContainer,containerManager是BoostContainerManager,它的showContainer:


void showContainer(BoostContainerSettings settings) {
    if (settings.uniqueId == _onstage.settings.uniqueId) {
      _onShownContainerChanged(null, settings.uniqueId);
      return;
    }
    final int index = _offstage.indexWhere((BoostContainer container) =>
        container.settings.uniqueId == settings.uniqueId);
    if (index > -1) {
      _offstage.add(_onstage);
      _onstage = _offstage.removeAt(index);
      setState(() {});
      for (final BoostContainerObserver observer in FlutterBoost
          .singleton.observersHolder
          .observersOf<BoostContainerObserver>()) {
        observer(ContainerOperation.Onstage, _onstage.settings);
      }
      Logger.log('ContainerObserver#2 didOnstage');
    } else {
      pushContainer(settings);
    }
  }
复制代码


如果该页面之前不存在,则执行pushContainer(settings):


void pushContainer(BoostContainerSettings settings) {
    assert(settings.uniqueId != _onstage.settings.uniqueId);
    assert(_offstage.every((BoostContainer container) =>
        container.settings.uniqueId != settings.uniqueId));
    _offstage.add(_onstage);
    _onstage = BoostContainer.obtain(widget.initNavigator, settings);
    setState(() {});
    for (final BoostContainerObserver observer in FlutterBoost
        .singleton.observersHolder
        .observersOf<BoostContainerObserver>()) {
      observer(ContainerOperation.Push, _onstage.settings);
    }
    Logger.log('ContainerObserver#2 didPush');
  }
复制代码


这里通过BoostContainer.obtain来创建一个widget并赋值给_onstage,这个函数源码:


factory BoostContainer.obtain(
      Navigator navigator,
      BoostContainerSettings settings,
      ) =>
      BoostContainer(
        key: GlobalKey<BoostContainerState>(),
        settings: settings,
        onGenerateRoute: (RouteSettings routeSettings) {
          if (routeSettings.name == '/') {
            return BoostPageRoute<dynamic>(
              pageName: settings.name,
              params: settings.params,
              uniqueId: settings.uniqueId,
              animated: false,
              settings: RouteSettings(
                name: settings.name,
                arguments: routeSettings.arguments,
              ),
              builder: settings.builder,
            );
          } else {
            return navigator.onGenerateRoute(routeSettings);
          }
        },
        observers: <NavigatorObserver>[
          ContainerNavigatorObserver.bindContainerManager(),
          HeroController(),
        ],
        onUnknownRoute: navigator.onUnknownRoute,
      );
复制代码


可以看到这就是通过我们熟悉的RouteFactory来创建widget,这样就实现了router,同时也实现的传参。


观察BoostContainerManager(container_mannager.dart)可以发现,_onstage是当前展示的页面,而_offstage则是前级页面,而布局时其实全部堆叠的:


final List<BoostContainer> containers = <BoostContainer>[];
containers.addAll(_offstage);
assert(_onstage != null, 'Should have a least one BoostContainer');
containers.add(_onstage);
复制代码


这样就通过改变_onstage和_offstage来实现页面的切换,所以flutter-boost本质上是用一个页面切换不同的内容,而所有页面都公用一个flutter engine(都是进一个页面,所以initialRoute固定),这样除了第一次打开,后面再次打开就会很快,实现的启动加速。


这样我们就大致的了解了flutter-boost的启动原理,当然flutter-boost还有很多功能,不过了解了这个启动原理,我们可以试着自己来实现一个简单的框架。


iOS


ios其实与android类似,ios与flutter的交互同样是上面三种方式。

在ios中我们通过FlutterViewController来展示flutter页面,可以参考《Flutter混合开发:在已有iOS项目中引入Flutter》,所以这个相当于android中的FlutterActivity,同时我们在上面知道最终是通过didShowPageContainer这个函数来展示页面,因为这个定义在flutter中,所以在ios层面应该也是调用这个函数,这样就比较简单了。


在flutter-boost的ios源码中我们可以找到FLBFlutterViewContainer.m(flutter_boost/ios/Classes/container/)这个文件,在这个文件中搜索didShowPageContainer发现下面的代码:


- (void)viewDidAppear:(BOOL)animated
{
    [FLUTTER_APP addUniqueViewController:self];
    //Ensure flutter view is attached.
    [self attatchFlutterEngine];
    [BoostMessageChannel didShowPageContainer:^(NSNumber *result) {}
                                           pageName:_name
                                             params:_params
                                           uniqueId:self.uniqueIDString];
    //NOTES:务必在show之后再update,否则有闪烁; 或导致侧滑返回时上一个页面会和top页面内容一样
    [self surfaceUpdated:YES];
    [super viewDidAppear:animated];
}
FLBFlutterViewContainer继承ios的UIViewController,见FLBFlutterViewContainer.h
#import <UIKit/UIKit.h>
#import <Flutter/Flutter.h>
#import "FLBFlutterContainer.h"
NS_ASSUME_NONNULL_BEGIN
@interface FLBFlutterViewContainer  : FlutterViewController<FLBFlutterContainer>
@property (nonatomic,copy,readwrite) NSString *name;
- (instancetype)init NS_DESIGNATED_INITIALIZER;
- (void)surfaceUpdated:(BOOL)appeared;
@end
NS_ASSUME_NONNULL_END
复制代码


viewDidAppear这个函数就是从他继承过来的,其生命周期与android中的resume类似,所以在这个阶段执行didShowPageContainer,在flutter中完成widget切换。


所以可以看到ios的原理与android基本类似,也是通过重写承载flutter页面的类,然后通过交互方式通知flutter,flutter中就是单页面切换widget的方式,这样就可以使用一个flutter engine,提前初始化并预热(ios中是运行)这个engine来提高加载效率。

通过上面的原理,我们可以自己开放一个简单的flutter启动plugin。


目录
相关文章
|
6月前
|
存储 移动开发 安全
Flutter加固原理及加密处理
Flutter加固原理及加密处理
105 0
|
6月前
|
JSON Dart 安全
Flutter App混淆加固、保护与优化原理
Flutter App混淆加固、保护与优化原理
116 0
|
6月前
|
存储 安全 数据安全/隐私保护
Flutter应用程序的加固原理
Flutter应用程序的加固原理
81 0
|
15天前
|
开发者 容器
Flutter&鸿蒙next 布局架构原理详解
本文详细介绍了 Flutter 中的主要布局方式,包括 Row、Column、Stack、Container、ListView 和 GridView 等布局组件的架构原理及使用场景。通过了解这些布局 Widget 的基本概念、关键属性和布局原理,开发者可以更高效地构建复杂的用户界面。此外,文章还提供了布局优化技巧,帮助提升应用性能。
78 4
|
15天前
|
存储 Dart 前端开发
flutter鸿蒙版本mvvm架构思想原理
在Flutter中实现MVVM架构,旨在将UI与业务逻辑分离,提升代码可维护性和可读性。本文介绍了MVVM的整体架构,包括Model、View和ViewModel的职责,以及各文件的详细实现。通过`main.dart`、`CounterViewModel.dart`、`MyHomePage.dart`和`Model.dart`的具体代码,展示了如何使用Provider进行状态管理,实现数据绑定和响应式设计。MVVM架构的分离关注点、数据绑定和可维护性特点,使得开发更加高效和整洁。
145 3
|
21天前
动画控制器在 Flutter 中的工作原理
【10月更文挑战第18天】总的来说,动画控制器 `AnimationController` 在 Flutter 中起着关键的作用,它通过控制动画的数值、速度、节奏和状态,实现了丰富多彩的动画效果。理解它的工作原理对于我们在 Flutter 中创建各种精彩的动画是非常重要的。
|
28天前
|
容器
Flutter&鸿蒙next 布局架构原理详解
Flutter&鸿蒙next 布局架构原理详解
|
5月前
|
存储 开发框架 JavaScript
深入探讨Flutter中动态UI构建的原理、方法以及数据驱动视图的实现技巧
【6月更文挑战第11天】Flutter是高效的跨平台移动开发框架,以其热重载、高性能渲染和丰富组件库著称。本文探讨了Flutter中动态UI构建原理与数据驱动视图的实现。动态UI基于Widget树模型,状态变化触发UI更新。状态管理是关键,Flutter提供StatefulWidget、Provider、Redux等方式。使用ListView等可滚动组件和StreamBuilder等流式组件实现数据驱动视图的自动更新。响应式布局确保UI在不同设备上的适应性。Flutter为开发者构建动态、用户友好的界面提供了强大支持。
98 2
|
4月前
|
Dart JavaScript Java
flutter 架构、渲染原理、家族
flutter 架构、渲染原理、家族
85 3
|
6月前
|
Android开发
Flutter完整开发实战详解(六、 深入Widget原理),2024百度Android岗面试真题收录解析
Flutter完整开发实战详解(六、 深入Widget原理),2024百度Android岗面试真题收录解析