FlutterBoost3.0发布preview版本

在经历了近两个月的开发以及内部测试与线上灰度,FlutterBoost3.0的preview版本终于与大家见面了,与beta版本相比,这个版本在不大动主体结构的基础上,增加了以下能力:

1.重构生命周期,确保生命周期语义准确

2.双端一致性近一步对齐

3.增加自定义的启动参数

4.实现页面返回传参方案

5.支持页面透明能力

6.增加自定义事件发送机制

7.增加前置拦截器

8.提供更完善的文档与例子

由于篇幅有限,就不对以上能力一一展开了。如果对具体实现感兴趣的话,可以通过源码和文档来了解相应的功能,我们今天先来聊一聊生命周期部分的设计与实现、文档与用例和社区建设。

页面生命周期设计

FlutterBoost3.0有两个和页面相关的概念,一个是BoostContainer,另一个是BoostPage。每个BoostContainer在Native层都会有一个与之对应的容器,比如在Android端,这个容器可能是FlutterBoostActivity或者FlutterBoostFragment,而在iOS端,这个容器则指的是FBFlutterViewContainer。每个BoostContainer都会有一个与之对应的Navigator,因而一个BoostContainer可以包含多个BoostPage,这个设计也被我们称之为双重栈。细心的同学可能已经发现了,当我们需要打开一个Flutter页面时,有一个额外的参数withContainer,这个参数设置为false时,表示打开页面时不需要打开一个新容器,而这个参数如果被设置为true,则会在打开页面的同时,去打开一个新容器。

FlutterBoost3.0发布preview版本_第1张图片

在开发业务的过程中,我们常常需要知道一个Flutter页面的可见性,比如我们写了一个Flutter页面,里面有一个VideoWidget,我们希望VideoWidget在页面可见的时候播放,在页面不可见的时候暂停。对页面可见性变化的监听是FlutterBoost需要提供的核心能力之一,因此我们提供了一个名为PageVisibilityObserver的API,用于专门监听页面可见性变化。

在一开始的设计当中,我们是通过上层Dart代码来掌控页面生命周期的。我们在FlutterBoostAppState里,维护了一个List,而在每个BoostContainer中,则维护了一个List,所以显示的页面实际上就是顶部BoostContainer中的顶部BoostPage,所以每次顶部的BoostContainer发生变化,或者顶部BoostContainer中的顶部BoostPage发生变化,我们就认为发生了页面可见性变化,并将相应事件分发下去。

然而这个方案在实践的过程中,发现了一些问题,由于Dart侧只对Flutter的双层栈有感知,而对Native侧的页面栈则是毫无感知的,所以哪怕一个页面是顶部BoostContainer中的顶部BoostPage,也不见得这个页面就处于显示状态。举个例子,比如Flutter页面A去打开一个Native页面B,那么在打开Native页面B之后,Flutter页面A还是在双重栈顶部,因此如果不做任何处理,那么FlutterBoost还是认为Flutter页面A是可见的。我们也尝试了一些手段去解决这些Bad Case,比如在Native层也维护一个Flutter页面栈用来感知页面显示隐藏等。但是在后续的验证过程中,我们发现这个方案可维护性不高,最终放弃了。

之后我们决定还是采用更稳妥的方案,BoostContainer的显示和隐藏,让Native容器自己来控制,当Native容器展示的时候发出onContainerShow事件通知对应的BoostContainer展示,Native容器隐藏时再发出onContainerHide事件通知对应的的BoostContainer隐藏,这样生命周期难以维护的问题也就解决了,这个方案的可维护性相比上一个方案要稳定,但是这个方案也有一个不得不解决的问题,第一次onContainerShow可能会收不到。我们以Android为例,FlutterBoostActivity创建后的第一次onResume发出了onContainerShow事件,然而该事件并不一定会被收到,因为此时可能页面对应的Widget还没有完成创建。稳定的生命周期事件是一个强诉求,所以这个问题我们需要在FlutterBoost3.0彻底解决。那么这个问题该如何解决呢?为了解决这个问题,我们将第一次收到onContainerShow事件单独处理,具体逻辑如下

void containerDidShow(BoostContainer container) {
    final id = container.pageInfo.uniqueId;
    assert(id != null);
    if (!hasShownPageIds.contains(id)) {
        hasShownPageIds.add(id);

        // This case indicates it is the first time that this container show
        // So we should dispatch event using
        // PageVisibilityBinding.dispatchPageShowEventOnPageShowFirstTime
        // to ensure the page will receive callback
        PageVisibilityBinding.instance
            .dispatchPageShowEventOnPageShowFirstTime(container.topPage.route);
    } else {
        PageVisibilityBinding.instance
            .dispatchPageShowEvent(container.topPage.route);
    }
}

而在PageVisibilityBinding.dispatchPageShowEventOnPageShowFirstTime中,我们将事件分发延迟到这一帧渲染结束

///When page show first time,we should dispatch event in [FrameCallback]
///to avoid the page can't receive the show event
void dispatchPageShowEventOnPageShowFirstTime(Route route) {
    WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
        dispatchPageShowEvent(route);
    });
}

这样我们就能确保第一次onContainerShow事件能够被接收到。从最后的代码来看,这是一个微不足道的改动,但是从设计上看,这个改动明确定义了页面生命周期事件,确保了生命周期事件一定被调用到。

Flutter应用生命周期设计

对于纯Flutter应用来说,一个Flutter应用实际上是跑在一个Activity或者ViewController上的,所以Flutter应用的生命周期实际上也和Activity或ViewController的生命周期绑定了,这也是Flutter目前的实现。但是对于单引擎多页面的混合栈场景来说,这无疑是不适用的。如果我们不对Flutter应用生命周期进行任何处理,那么就有可能出现Flutter应用生命周期混乱,最终导致界面无法刷帧。之前我们解决这个问题的思路,都是从Native层出发,通过控制生命周期事件的发送,来校正Flutter应用的生命周期。比如Android端,就是在每次Flutter应用被pause的时候,补发一个resume事件,而iOS端则是绕过FlutterViewController生命周期方法来自行管理生命周期事件。这样做虽然校正了Flutter应用的生命周期,但最终也导致了上层应用的生命周期非常混乱,可能会出现pause和resume连续多次被调用的情况。

为了上层使用者能有一个稳定的Flutter应用生命周期,我们决定将这部分能力接管。我们提供了一个BoostFlutterBinding,用于接管生命周期,相关代码如下

mixin BoostFlutterBinding on WidgetsFlutterBinding {

  bool _appLifecycleStateLocked = true;

  @override
  void initInstances() {
    super.initInstances();
    _instance = this;
    changeAppLifecycleState(AppLifecycleState.resumed);
  }

  static BoostFlutterBinding get instance => _instance;
  static BoostFlutterBinding _instance;

  @override
  void handleAppLifecycleStateChanged(AppLifecycleState state) {
    if (_appLifecycleStateLocked) {
      return;
    }
    Logger.log('boost_flutter_binding: handleAppLifecycleStateChanged ${state.toString()}');
    super.handleAppLifecycleStateChanged(state);
  }

  void changeAppLifecycleState(AppLifecycleState state) {
    if (SchedulerBinding.instance.lifecycleState == state) {
      return;
    }
    _appLifecycleStateLocked = false;
    handleAppLifecycleStateChanged(state);
    _appLifecycleStateLocked = true;
  }
}

代码非常简单,就是将handleAppLifecycleStateChanged方法给hook掉,这样无论Native层如何调用Flutter设定的生命周期事件,都不会影响到上层Flutter应用的生命周期。另外我们提供了一个changeAppLifecycleState方法,这个方法可以真正来改变上层Flutter应用的生命周期,目前这个方法的调用时机与Flutter容器个数相关,当容器大于等于1,Flutter应用的生命周期状态为resumed,而容器个数为0时,Flutter应用的生命周期状态则为paused。

文档与用例完善

FlutterBoost在之前只有一份接入文档,这对使用者来说并不算友好。因此我们决定提供更完善的文档。我们梳理了大家最关心的几个主题:

•集成详细步骤

•基本的路由API

•页面生命周期监测相关API

•自定义发送跨端事件API

对于这些主题,我们这次都提供了相对应的文档,让使用者接入FlutterBoost可以更轻松,在遇到问题的时候也能更容易排查。另外我们也正在编写全新的example3.0,配合文档使用,在这个新的example3.0中,我们会把所有文档中提到的部分都以用例的方式呈现出来,希望这能帮助到大家。

社区建设

之后Boost所有的开发计划工作,都会在project看板上体现出来,对应的地址为( https://github.com/alibaba/flutter_boost/projects)。对我们工作感兴趣的同学,可以在看板上看到我们的工作规划,欢迎一起交流探讨。另外如果有哪些想要我们支持的feature,也可以提issue给我们,如果我们评估合理,也会加入到看板上。

FlutterBoost3.0发布preview版本_第2张图片

于此同时,我们增加了一个AUTHORS文件(https://github.com/alibaba/flutter_boost/blob/master/AUTHORS),记录所有FlutterBoost的贡献者(Contributors)。之后给我们提PR的同学,在提供PR的同时,也可以在这个文件上,加上你的名字,感谢大家为Boost做出的贡献。

未来展望

FlutterBoost3.0作为AliFlutter的核心基础设施,目前主要由闲鱼团队和UC Hummer团队进行开发维护,主要开发者包括noborder、0xZOne、christyuj、ColdPaleLight、luckysmg,另外也要感谢seedotlee、CheungSKei、bktoky、jk等同学为FlutterBoost3.0做出的贡献,目前集团内已有多个App接入了FlutterBoost3.0,包括夸克、淘宝联盟、吃货笔记等。

之后FlutterBoost3.0的preview版本原则上不会再做Breaking Change,目前已经使用了beta版的同学,也可以陆续切到preview版本上了,下个阶段我们会将工作重心放在issue收敛与文档用例补齐上。

FlutterBoost作为一个开源项目,还有很长的路要走,感谢大家一路支持和包容,我们也希望有更多同学能参与到这个项目中来。

你可能感兴趣的:(java,编程语言,github,css,html)