flutter_boost 源码解读

1,710 阅读4分钟

runApp之后会做两件事情。
1)注册widget跟对应的key

FlutterBoost.singleton.registerPageBuilders({
      'first': (pageName, params, _) => FirstRouteWidget(),
      'second': (pageName, params, _) => SecondRouteWidget(),
      },
    });
    

2)初始化widget

@override
  Widget build(BuildContext context) {
    return MaterialApp(
        title: 'Flutter Boost example',
        builder: FlutterBoost.init(postPush: _onRoutePushed),
        home: Container());
  }

首先我们来看FlutterBoost单例实现FlutterBoost.singleton。 首先创建BoostChannel,BoostChannel内创建flutter_boost 的MethodChannel。设置MethodCallHandler。 然后通过channel初始化ContainerCoordinator(协调者)

ContainerCoordinator(BoostChannel channel) {
    assert(_instance == null);

    _instance = this;

//添加生命周期监听
    channel.addEventListener("lifecycle",
        (String name, Map arguments) => _onChannelEvent(arguments));

//添加事件监听
    channel.addMethodHandler((MethodCall call) => _onMethodCall(call));
  }

在iOS端会invoke以下几种类型。 生命周期:

- (void)didEnterBackground
{
    [BoostMessageChannel sendEvent:@"lifecycle"
                         arguments:@{@"type":@"background"}];
}

- (void)willEnterForeground
{
    [BoostMessageChannel sendEvent:@"lifecycle"
                         arguments:@{@"type":@"foreground"}];
}

//最终都会以__event__为method传递到dart
+ (void)sendEvent:(NSString *)eventName
        arguments:(NSDictionary *)arguments
{
    if(!eventName) return;
    NSMutableDictionary *msg = [NSMutableDictionary new];
    msg[@"name"] = eventName;
    msg[@"arguments"] = arguments;
    [self.methodChannel invokeMethod:@"__event__"
                 arguments:msg
                    result:^(id r){}];
}

事件:

  [self.methodChannel invokeMethod:@"onNativePageResult" arguments:tmp result:^(id tTesult) {
         if (result) {
             result(tTesult);
         }
     }];

[self.methodChannel invokeMethod:@"didShowPageContainer" arguments:tmp result:^(id tTesult) {
         if (result) {
             result(tTesult);
         }
     }];
//类似的event还有很多,就不一一列举了

//dart端都有自定义的类型与之对应
enum ContainerLifeCycle {
  Init,
  Appear,
  WillDisappear,
  Disappear,
  Destroy,
  Background,
  Foreground
}

在MethodCallHandler闭包回调的时候会根据是call.method是否是event来判断执行EventListener的监听闭包_onChannelEvent,还是MethodHandler的的监听闭包_onMethodCall。

接下来让我们来认识下MaterialApp。 MaterialApp的child为WidgetsApp,WidgetsApp内创建了Navigator。

FlutterBoost.init()的时候创建了BoostContainerManager,BoostContainerManager的widget是Overlay。Overlay作用是创建叠加层。

@override
  Widget build(BuildContext context) {
    return Overlay(
      key: _overlayKey,
      initialEntries: const <OverlayEntry>[],
    );
  }

ContainerManagerState内主要创建了以下内容:

class ContainerManagerState extends State<BoostContainerManager> {
  //定义了globalKey _overlayKey可以通过_overlayKey.currentState 更新widget,达到界面更换的效果
  final GlobalKey<OverlayState> _overlayKey = GlobalKey<OverlayState>();
  //不在当前页面的容器
  final List<BoostContainer> _offstage = <BoostContainer>[];
//最近的overlayEntry 数组 显示的页面   _ContainerOverlayEntry的builder为每一个页面对应的BoostContainer。所以通过修改_leastEntries可以达到想要的页面切换效果。
  List<_ContainerOverlayEntry> _leastEntries;
//当前展示的容器内容
  BoostContainer _onstage;
}

接下来我们简单根据iOS唤起的逻辑来看一下flutter_boost如果指定widget进行显示。 当iOS FLBFlutterViewContainer 调用viewWillAppear的时候。

[BoostMessageChannel willShowPageContainer:^(NSNumber *result) {}
                                            pageName:_name
                                              params:_params
                                            uniqueId:self.uniqueIDString];

 + (void)willShowPageContainer:(void (^)(NSNumber *))result pageName:(NSString *)pageName params:(NSDictionary *)params uniqueId:(NSString *)uniqueId
 {
     if ([pageName isEqualToString:kIgnoreMessageWithName]) {
         return;
     }
     NSMutableDictionary *tmp = [NSMutableDictionary dictionary];
     if(pageName) tmp[@"pageName"] = pageName;
     if(params) tmp[@"params"] = params;
     if(uniqueId) tmp[@"uniqueId"] = uniqueId;
     [self.methodChannel invokeMethod:@"willShowPageContainer" arguments:tmp result:^(id tTesult) {
         if (result) {
             result(tTesult);
         }
     }];
 }

当flutter端监听到时候会根据method调用到

bool _nativeContainerWillShow(String name, Map params, String pageId) {
   if (FlutterBoost.containerManager?.containsContainer(pageId) != true) {
     FlutterBoost.containerManager
         ?.pushContainer(_createContainerSettings(name, params, pageId));
   }

void pushContainer(BoostContainerSettings settings) {
  //添加新的容器 确保新的容器不在_onstage跟_offstage中
   assert(settings.uniqueId != _onstage.settings.uniqueId);
   assert(_offstage.every((BoostContainer container) =>
       container.settings.uniqueId != settings.uniqueId));
   _offstage.add(_onstage);
 // 根据settings获取到当前应该展示的容器
   _onstage = BoostContainer.obtain(widget.initNavigator, settings);
   setState(() {});
 }

setState((){})之后会调用_refreshOverlayEntries。

void _refreshOverlayEntries() {
   //获取到globalkey对应的overlayState
    final OverlayState overlayState = _overlayKey.currentState;

    if (overlayState == null) {
      return;
    }
    //先移除所有的_ContainerOverlayEntry
    if (_leastEntries != null && _leastEntries.isNotEmpty) {
      for (_ContainerOverlayEntry entry in _leastEntries) {
        entry.remove();
      }
    }
   
    final List<BoostContainer> containers = <BoostContainer>[];
    //先添加当前不显示的
    containers.addAll(_offstage);

    assert(_onstage != null, 'Should have a least one BoostContainer');
    //再添加当前要显示的
    containers.add(_onstage);
    //转换成_ContainerOverlayEntry,_ContainerOverlayEntry的builder是BoostContainer
    _leastEntries = containers
        .map<_ContainerOverlayEntry>(
            (BoostContainer container) => _ContainerOverlayEntry(container))
        .toList(growable: false);
    //栈结构 最后面添加的即是要显示的 也就是_onstage
    overlayState.insertAll(_leastEntries);
}

目前我们就可以通过以下方法在iOS端展示我们想要的容器:

[FlutterBoostPlugin open:@"first" urlParams:@{kPageCallBackId:@"MycallbackId#1"} exts:@{@"animated":@(YES)} onPageFinished:^(NSDictionary *result) {
        NSLog(@"call me when page finished, and your result is:%@", result);
    } completion:^(BOOL f) {
        NSLog(@"page is opened");
    }];

最终都会走到实现FLBPlatform协议的方法内:

- (void)open:(NSString *)name
   urlParams:(NSDictionary *)params
        exts:(NSDictionary *)exts
  completion:(void (^)(BOOL))completion;

- (void)present:(NSString *)name
   urlParams:(NSDictionary *)params
        exts:(NSDictionary *)exts
  completion:(void (^)(BOOL))completion;

在iOS容器FLBFlutterViewContainer内我们之前分析了ViewWillAppear,我们再继续分析下dealloc,毕竟涉及到内存相关是我们比较关心的部分。

- (void)dealloc
{
    [self notifyWillDealloc];
    [NSNotificationCenter.defaultCenter removeObserver:self];
}

- (void)notifyWillDealloc
{
    [BoostMessageChannel willDeallocPageContainer:^(NSNumber *r) {}
                                               pageName:_name params:_params
                                               uniqueId:[self uniqueIDString]];

    [FLUTTER_APP removeViewController:self];
    [self.class instanceCounterDecrease];
}

 + (void)willDeallocPageContainer:(void (^)(NSNumber *))result pageName:(NSString *)pageName params:(NSDictionary *)params uniqueId:(NSString *)uniqueId
{
     [self.methodChannel invokeMethod:@"willDeallocPageContainer" arguments:tmp result:^(id tTesult) {
         if (result) {
             result(tTesult);
         }
     }];
 }

flutter端监控到willDeallocPageContainer调用以下方法:

bool _nativeContainerWillDealloc(String name, Map params, String pageId) {
    FlutterBoost.containerManager?.remove(pageId);
    return true;
  }

但是这个时候我们要注意在iOS端生命周期函数 当first_VC push到second_VC的时候再从second_VC返回的时候会按照以下顺序调用 1、second_VC的ViewWillDisappear 2、first_VC viewWillAppear 3、second_VC viewDidDisappear 4、first_VC viewDidAppear 5、second_VC dealloc

//所以我们会先调用first_VC对于的容器对应的showContainer。
void showContainer(BoostContainerSettings settings) {
    //这个时候不想等 跳到下面
    if (settings.uniqueId == _onstage.settings.uniqueId) {
      _onShownContainerChanged(null, settings.uniqueId);
      return;
    }

     //从_offstage内找到即将要显示的容器赋值给_onstage
    final int index = _offstage.indexWhere((BoostContainer container) =>
        container.settings.uniqueId == settings.uniqueId);
    if (index > -1) {
      _offstage.add(_onstage);
      _onstage = _offstage.removeAt(index);
      setState(() {});
    } else {
      pushContainer(settings);
    }
  }

然后监听dealloc调用到remove 在_offstage中remove之前的container。

void remove(String uniqueId) {
    if (_onstage.settings.uniqueId == uniqueId) {
      pop();
    } else {
      final BoostContainer container = _offstage.firstWhere(
          (BoostContainer container) => container.settings.uniqueId == uniqueId,
          orElse: () => null);

      if (container != null) {
        _offstage.remove(container);
        setState(() {});
      }
    }
    return null;
  }

flutter_boost相关源码就暂且分析到这了。