Flutter在流式场景下的架构设计与应用

简介: 减少发版依赖,提升研发效能
+关注继续查看

作者:闲鱼技术——光酒

目前,闲鱼的主要业务场景都是基于流式场景构建的。在闲鱼的主要几个业务场景下存在两种类型的页面:一种是复杂交互的页面,如发布页面、商品详情页;另一种是轻交互、需要一定动态化能力满足千人千面的运营配置及快速A/B实验需求的页面,如首页、搜索页面、我的等页面。

在这些轻交互、动态化运营的页面场景下,有很多共通的处理逻辑:页面的布局、数据的管理、事件逻辑驱动的数据变化以及数据驱动的视图状态更新;这些工作往往大部分都是重复的工作,重复的代码逻辑。

在研发效能、交付效率方面,业务的变化往往依赖于版本发布,动辄两周的发版周期,对于需要快速投放和响应的业务来说,上线时间过长将难以接受。

能否设计一种流式页面搭建能力,实现页面的快速搭建,减少重复代码,提升研发效能;提供业务动态化的能力,减少对发版发布的依赖,提高上线的交付效率?

为了解决以上问题,在Flutter版本首页改版的契机下,闲鱼设计了一套流式场景下的页面搭建架构设计。

流式页面容器架构设计

在流式布局的架构设计过程中,面对实际的业务场景,通过以下几个方面解决端到端的流式页面容器设计:

  1. 在搭建平台侧,实现页面搭建、组件管理、协议编排等能力,与投放平台、A/B实验平台和监控平台打通;
  2. 在客户端侧,采用MVVM模型,设计通用的事件协议,抽象通用的页面布局、数据管理及事件处理的能力,减少重复的代码开发,提升研发效率。在页面布局管理方面,与列表容器PowerScrollView深度结合,实现高效的页面渲染、数据驱动的页面刷新能力;
  3. 使用阿里巴巴集团 DinamicX作为DSL实现动态模板渲染,满足投放以及运营需求;
  4. 在与服务端通信协议方面,闲鱼一直在实践Flutter+FaaS的云端一体化开发,借助FaaS的能力,定义一套云端一体化的事件协议,解决业务逻辑动态化的问题,减少发版依赖,进而提升交付效率。

arch

在流式页面容器架构设计中,重点包括以下几个核心模块:协议层、事件中心和数据中心。下面介绍这几个模板的详细设计。

协议的设计

在页面容器协议的设计方面,在结合闲鱼业务以及阿里巴巴集团的一些技术方案后,闲鱼采用了三层协议的设计:Page、Section和Component。

  1. Page层协议主要包含整个页面Sections信息,以及下拉刷新、上拉加载更多等配置信息;
  2. Section层协议包含当前Section的布局信息、初始化Event、LoadMore Event及Components等信息;
  3. Component层协议与具体业务相关,对于容器来说是黑盒的,具体如何渲染会交给业务方处理;默认提供DX解析渲染Handler。

protocol

在通信协议的设计上,全部采用事件传递的方式,包括:客户端与服务端、组件与组件、页面与组件、页面与App之间。这也是云端一体化的设计,理论上开发者只需要考虑事件的发送与接收,具体事件的处理在客户端还是在服务端,由对应的Handler决定。在云端一体化的设计下,事件的处理更加灵活,可以更方便地将逻辑后移,当业务发生变更时,减少对发版的依赖。

接下来就让我们来具体看一看事件中心的设计。

事件中心的设计

一切皆是Event;

在PowerContainer的设计中,一切皆是事件:不论是数据的更新、消息的传递、网络请求、服务端返回的结果,还是自定义的本地处理逻辑。闲鱼抽象定义了八种通用的事件类型,整个页面容器通过事件的流动,完成页面UI的渲染和刷新,以及业务逻辑的表达和执行。

event

以一次网络请求为例,一次下拉刷新会获取每个Section的initEvent事件,并添加到事件中心;事件中心根据事件类型找到对应的Handler来处理。

如果initEvent配置的是remote请求,则交给remoteHandler发送网络请求,将事件传送给FaaS端;在FaaS端收到Event后,在FaaS端的事件中心分发,找到对应的hsf服务并获取数据,最后拼装成Event的方式,下发给客户端;客户端接收到之后继续让Event在事件中心流动起来。

在处理完远端下发的事件之后,EventCenter会发送事件结束的广播,便于业务处理相关自定义事件。

event remote

通用事件抽象

下面我们来具体看一看通用事件的抽象:

  1. Restart事件:指定整个Page或者某个Section的刷新事件,对于需要刷新的Section,会将其initEvent事件加入事件中心。initEvent常见的一般为一个Remote事件,也可以是任意其他事件。
  2. LoadMore事件:LoadMore事件主要处理分页加载更多数据的场景。
  3. Update事件:Update事件主要处理数据源的更新及UI的刷新。
  4. Context更新事件:每个Section都存在一个Context信息,代表了服务端与客户端请求的上下文信息;每个Section的Rmote事件请求,都会默认将Context信息发送给服务端,相应的服务端可以下发Context事件更新指定Section的Context信息;具体使用场景例如分页加载的page number等;
  5. Replace事件:Replace事件替换Section信息,在tab切换等场景使用会使用;
  6. Remote事件:远端请求事件;
  7. Native事件:本地通用事件,如页面跳转、toast提示、数据埋点等;
  8. Custom事件:版本预埋的业务自定义事件。

数据中心的设计

在MVVM架构中,数据中心承担着ViewModel的角色,处理Update事件,主要负责数据的更新及UI视图的刷新。对于数据的Update事件,闲鱼根据自身业务场景抽象了几种通用的数据更新类型:overload、patch、override和remove。在UI渲染方面,闲鱼将列表容器PowerScrollView与动态模板渲染DXFlutter相结合,实现页面渲染及数据更新后的页面刷新能力。

列表容器

PowerScrollView是闲鱼实现的一套功能完善、高性能的列表布局容器,满足了页面容器对于瀑布流、卡片曝光、锚点定位等能力的需求。在视图渲染刷新方面,PowerScrollView提供了列表的局部刷新能力,完美地解决了数据更新后视图的刷新问题。

在协议设计上,二级协议Section以及Footer、Header的设计与PowerScrollView的设计是一一对应的。二级协议Section定义了唯一标识Key,在UI渲染中,对应到PowerScrollView的SectionKey。在数据更新后,页面容器会根据Section Key实现视图的局部刷新能力。

render

关于PowerScrollView的详细设计和介绍可以参考闲鱼技术公众号的文章。

动态模板渲染

DXFlutter使用阿里巴巴集团DinamicX作为DSL,在Flutter端实现了高效的动态模板渲染的能力。闲鱼使用DXFlutter实现Component层协议的动态模板渲染。

在介绍协议设计时提到过,Component层协议对于页面容器来说是黑盒,那么DX卡片事件是如何与页面容器PowerContainer打通的呢?黑盒的数据又是如何更新的呢?

event

在DSL中,闲鱼自定义了页面容器PowerContainer的事件powerEvent,通过它可以生成页面容器的通用事件类型,将DinamicX卡片的事件与页面容器事件中心打通。以上面代码为例,点击“删除关注列表里面的推荐卡片”的场景,只需要在onTap的事件中定义一个update类型的事件,subType为remove,即可实现数据的删除及删除后UI的渲染。

然而这里没有定义任何标识,且列表中可以存在多个相同的卡片,又是怎么知道要操作的是哪一份数据呢?

这里为每个Component生成一个唯一的ComponentKey,根据SectionKey+ComponentKey生成卡片的唯一标识。在每一个powerEvent事件中,会将Key传入事件中心,这样就定位到任意一个Component的数据model,根据事件类型更新数据model。同时,PowerScrollView也可以通过这个Key,操作UI的局部刷新。

Section状态管理

页面加载的过程中,往往需要展示一些加载状态的处理,如加载中的Loading动画、加载失败状态的重试按钮、没有更多内容状态的提示信息等。

在协议的设计方面,每个Section定义了state,在事件中心处理Remote请求事件和应答事件时,更新Section 的state。通过注册render handler,针对Section的不同状态返回加载状态Widget。

void updateSectionState(String key, PowerSectionState state) {
    final SectionData data = _dataCenter.findSectionByKey(key);
    if (state == PowerSectionState.loading) {
        // 从ViewCenter的config获取loadingWidget
        final Widget loadingWidget = _viewCenter?.config?.loadingWidgetBuilder(this, key, data);
        // ViewCenter调用replace section方法更新UI
        _viewCenter.replaceSectionOfIndex(loadingWidget);
        // 标记需要刷新Section
        data.needRefreshWhenComplete = true;
    } else if (state == PowerSectionState.error) {
        ...
    } else if (state == PowerSectionState.noMore) {
        ...
    } else if (state == PowerSectionState.complete) {
        if (data.needRefreshWhenComplete ?? false) { // 判断是否需要更新Section
            final int index = _dataCenter.fineSectionIndexByKey(key);
            if (index != null) {
                final SectionData sectionData = _dataCenter.containerModel.sections[index];
                final PowerSection section = _viewCenter.initSection(sectionData);
                _viewCenter.replaceSectionOfIndex(index, section);
            }
            data.needRefreshWhenComplete = false;
        }
    }
}

Section状态变化之后,通过PowerScrollView提供的replaceSection方法,刷新UI视图。

Tab容器支持

在闲鱼首页的场景,页面容器需要支持Tab容器的布局能力。PowerContainer又是如何支持Tab容器的支持呢?

闲鱼在Section的协议中引入了插槽(Slot)的概念,当搭建页面时,会指定Tab容器的Slot Section,默认不展示任何信息的空插槽。每一次切换Tab容器,通过Replace事件修改页面容器的Section信息。

void replaceSections(List<dynamic> sections) {
    if (sections == null || sections.isEmpty || _dataCenter?.containerModel?.sections == null) {
        return;
    }
    for (int i = 0; i < sections.length; i++) {
        SectionData replaceData = sections[i];
        assert(replaceData.slot != null);
        // 寻找Section list中与Slot匹配的index
        int slotIndex = _findSlot(replaceData);
        // 更新dataCenter
        _dataCenter.replaceSectionData(slotIndex, replaceData);
        // SectionData 转换为PowerScrollView所需的PowerSection
        final PowerSection sectionData = _viewCenter?.convertComponentDataToSection(replaceData);
        // 更新viewCenter
        _viewCenter?.replaceSectionOfIndex(slotIndex, sectionData);
        //将替换Section的Restart事件发送到event center
        sendEventRestart(replaceData.key);
    }
}

PowerScrollView同样也提供了replaceSection方法,与上文提到的Section状态管理相结合,完美地解决了tab容器的切换和加载状态管理的问题。

总结和展望

本节主要介绍了在轻交互、动态化运营场景下,如何从页面搭建、协议设计、端侧容器的实现、动态模板渲染、云端一体的事件交互等方面,设计并实现一套流式页面搭建能力,实现页面的快速搭建,提升研发效能。同时,提供业务动态化的能力,减少发版发布的依赖,提高上线的交付效率。

目前,页面容器PowerContainer在闲鱼首页Flutter版本重构中设计并落地。使用PowerContainer后,极大地降低了首页三个tab页面的重复代码,代码逻辑统一管理,降低了一半的人日工作量。在性能方面,有了PowerScrollView的局部刷新、Element复用、分帧渲染、差值器等方面的优化,在流畅度方面要优于原生的体验。

没有银弹!这样一套页面容器的设计并不是为了适用所有的业务场景,更适合以展示为主、轻交互动态化运营的业务场景。云端一体的事件协议,在服务端需要事件协议的封装,这也使得与Serverless的场景更加适合。

闲鱼未来还会在搭建平台侧做更多的尝试,真正实现所见即所得的快速页面搭建能力。在业务逻辑动态化方面,目前更多的是通过与FaaS的结合、逻辑后移的方式解决。但目前仍然无法解决本地自定义事件依赖发版的问题,未来在这方面也会有更多的尝试,做到 less code甚至是no code的业务开发。

相关实践学习
基于函数计算一键部署掌上游戏机
本场景介绍如何使用阿里云计算服务命令快速搭建一个掌上游戏机。
建立 Serverless 思维
本课程包括: Serverless 应用引擎的概念, 为开发者带来的实际价值, 以及让您了解常见的 Serverless 架构模式
相关文章
|
28天前
|
开发框架 监控 测试技术
Flutter系列文章-Flutter在实际业务中的应用
通过这个示例,你将更深入地了解如何在实际业务中应用Flutter,以及如何运用不同的解决方案和技术来构建高效、稳定的应用。
24 1
|
1月前
|
数据库连接 UED
Flutter系列文章-Flutter应用优化
当涉及到优化 Flutter 应用时,考虑性能、UI 渲染和内存管理是至关重要的。在本篇文章中,我们将通过实例深入讨论这些主题,展示如何通过优化技巧改进你的 Flutter 应用。
43 0
|
1月前
|
Dart IDE 开发工具
【腾讯云 Cloud Studio 实战训练营】尝鲜体验Flutter编写一个App应用
欢迎参加腾讯云 Cloud Studio 实战训练营!在本次训练营中,我们将通过App项目入口说明,基本文件说明,基础框架搭建,带您一步步编写一个基于 Flutter 的静态App系统。无论您是初学者还是有一定编程经验的开发者,本训练营都将为您提供一个深入了解和掌握 Flutter 技术以及App开发的机会。 使用 Flutter作为UI框架,我们将能够充分利用其强大的功能和优势,快速搭建一个高效、可扩展的App系统。Flutter是Google开源的构建用户界面(UI)工具包,帮助开发者通过一套代码库高效构建多平台精美应用,支持移动、Web、桌面和嵌入式平台。
【腾讯云 Cloud Studio 实战训练营】尝鲜体验Flutter编写一个App应用
|
2月前
|
开发框架 Dart 前端开发
从零到应用:我的Flutter项目开发之旅
Flutter是一种流行的跨平台移动应用开发框架,由Google推出。它使用Dart编程语言,通过单一代码库可以同时构建iOS和Android应用。Flutter具有许多吸引力的特性,如快速的渲染性能、漂亮的用户界面、丰富的组件库以及热重载等。通过阅读这篇文章,你将获得一些关于Flutter项目开发的实际指导,可以帮助你更有效地构建高质量的移动应用程序。无论你是初学者还是有一定经验的开发者,希望这些笔记能够为你提供一些有用的思路和技巧,让你在Flutter项目开发中取得更好的成果。
|
2月前
|
Dart C++ JavaScript
《深入浅出Dart》编写第一个Flutter应用
编写第一个Flutter应用 在本文中,我们将详细介绍如何编写你的第一个Flutter应用程序:一个简单的Hello World应用。我们将使用Dart语言和Flutter框架来创建一个具有基本用户界面的应用。 步骤 1:安装 Flutter 和 Dart 首先,确保你已经安装了Flutter和Dart的开发环境。可以参考之前提供的安装指南。
51 0
|
2月前
|
Dart
《深入浅出Dart》Flutter实战之TODO应用
Flutter实战之TODO应用 在本篇文章中,我们将构建一个TODO应用,使用最新的Dart语法和Flutter框架。这个TODO应用将具备添加、编辑、删除任务,以及任务的状态管理等功能。让我们一步一步地构建这个TODO应用。 步骤 1:创建Flutter项目 首先,我们需要创建一个Flutter项目。打开终端并执行以下命令:
68 0
|
4月前
|
编解码 Dart 安全
Flutter 环境搭建 与 Android 应用的调试
本文介绍 Flutter 环境搭建 与 Android 应用的调试
220 0
Flutter 环境搭建 与 Android 应用的调试
|
5月前
|
开发工具 Android开发 iOS开发
Flutter应用如何打包发版
Flutter应用程序的打包和发布可以通过Flutter命令行工具完成。
Flutter应用如何打包发版
|
6月前
|
缓存 Dart JavaScript
[AliFlutter]Flutter for Web在无影中的应用
无影是使用Flutter的重度用户,无论是在成熟的移动Android、iOS上,还是桌面端MacOS、Windows、还有各种硬件终端上(Linux)上都有应用 ![](https://ata2-img.oss-cn-zhangjiakou.aliyuncs.com/neweditor/c2434612-86ee-4fb6-a7d1-1622eb6d050d.png) 今年无影使用Flutt
307 0
[AliFlutter]Flutter for Web在无影中的应用
|
9月前
|
Dart IDE 开发工具
Flutter如何调试应用【Dart Observatory 】以及调试模式断言
Flutter如何调试应用【Dart Observatory 】以及调试模式断言
热门文章
最新文章
推荐文章
更多