有赞移动消息卡片动态化方案实践

2020 年 8 月 08 日

有赞移动消息卡片动态化方案实践

概述

消息业务作为有赞移动的共享业务,在微商城、零售、美业等 B 端 App 中承担着多客服的角色,多客服是有赞为商家提供的连接商家和买家的即时消息客服工具;在精选、有赞客 C 端产品中扮演着用户联系商家的角色。在整个有赞产品中,是商家和用户沟通的桥梁,起着非常重要的作用。

痛点

我们通常来讲把出现在消息会话页面内的内容称做消息卡片,目前消息业务常见的消息卡片有文字、富文本、语音、照片、视频、通知消息,除此之外还有订单详情、推荐商品、核对订单等共计 30 余种消息卡片。目前我们消息卡片都是采用原生来开发,随着业务的日益增多,消息卡片的数量也在成倍的增长,业务方经常提出增加卡片的需求,去开发和维护这些卡片工作量比较大,并且依赖业务方的客户端发版,不够灵活。

现状

目前消息业务在 iOS 端和 Android 端均采用二方库的方式开发,供各个有赞的 App 接入,在 iOS 中采用 Cocoapods 进行二方库的依赖,Android 采用 gradle 来进行二方库的依赖;如果业务方需要增加一种消息卡片从开发到上线按照正常的周期是 1 周到 1 周半,发版周期长、开发效率低。

动态化技术指不依赖 APP 发版,就能进行动态的增加或者修改来更新页面的技术。对于消息卡片这种需要快速迭代、实时调整的业务,动态化具有非常重要的意义。主要优势体现在:

  • 提高人效
  • 缩短版本迭代试错周期
  • 解决版本长尾问题
  • 减少包大小等

整个消息的业务场景如下图所示:

消息聊天以及消息模版都是由消息卡片组成

原生消息卡片

先来说说原生消息卡片的实现方案,这里以 iOS 为例。整体架构设计我们采用 TableView 作为整个页面的容器, 采用 MVVM 的架构去设计,架构分为以下模块:

  • 消息卡片数据源配置类 Configurator 负责 VC 和数据源处理类的绑定以及消息管理处理
  • 消息卡片接口管理类负责接收 / 发送消息数据处理,重发的操作、界面接口(增、删、改、查)
  • 数据源操作类 TableAdapter 负责 TabelView 的代理方式、数据源代理方法的实现,以及一些视图刷新等相关操作
  • 消息卡片内容视图工厂类 ContentFactory 会根据消息类型注册对应的 ContentConfig 进行一个缓存
  • 消息卡片配置类 ContentConfig 是根据消息类型从工厂类中获取的,返回当前消息卡片的宽高(size)、消息体 message、当前渲染的消息卡片视图
  • 消息模型 layout ,根据不同的消息类型生成,会保存当前的消息体以及消息卡片的宽高,为了提升性能,缓存在 TableAdapter 数据源操作类中
  • 消息卡片 cell,根据后端下发不同的卡片类型,从消息卡片配置类中获取不同的 contentView 加到 cell 中,进行消息卡片的展示
  • 消息卡片 contentView,负责消息卡片 UI 布局、数据的渲染、绑定

整个架构如下图所示:

消息原生架构图

方案

关于动态化技术栈的选择

上面介绍了 iOS 端消息卡片渲染架构设计,那么为了让消息卡片具有动态化的能力,但是不会打乱现有原生架构的情况下,我们采用了结合 weex 技术栈来做这件事。为什么用 weex 来提供动态化这样的能力?首先该技术栈已经在有赞移动中扮演者重要的角色,我们团队之前已经做了 weex 无线开发平台,我们团队主导建设了 ZanWeex ,它是一整套解决方案,从开发、构建到发布、热修、数据、监控,全生命周期的平台和工具,目前有赞业务很多模块都是以 weex 来实现,也接入了长连接功能,无须重启 App 实现实时下发的功能,对 weex 有兴趣 的,可以查阅 weex 官网。

因此我们的核心思路是利用 weex 来达到消息动态化的目的。

  • 创建 weex 容器,为了避免容器臃肿, 这里我们没有选择 ViewController 来充当 weex 的容器,而是用 View 来充当容器,里面包裹了一个 WXSDKInstance 进行 weex 的渲染。
  • 原生端创建渲染 weex 的 TableViewCell, 将 weex 容器视图添加到 cell 的 contentView 中去,利用 TableView 的重用机制进行 cell 的缓存和重用。

动态化能力

技术架构设计

在架构设计上面,我们在以前原生的 MVVM 的基础上,利用 JS 动态库,配合 weex 达到动态化卡片的能力,做到了 App 不发版的情况下,只需发布 weex 就可以动态添加消息卡片,也可以修改消息卡片的 UI 样式。鉴于这样的设计目标,在这个框架里,主要考虑以下方面:

  • 在不改变原有原生架构的基础上去做消息卡片动态化
  • 页面布局动态化,意思是页面的排版布局,可以通过 weex 端和 JS 端发布来达到动态更新
  • 组件的复用,为了在多种类型的卡片中保持良好的性能,需要对 weex 端容器进行缓存和复用
  • 开发、构建、发布整个流程需要拥有一个完善的平台

JS 动态库

JS 动态库是我们团队出的一个动态化方案的框架,主要功能是提供动态下发的能力;快速修复线上问题;两端特殊复杂的逻辑保持一致等。JS 动态库也是利用 ZanWeex 平台来开发、构建到发布,一整套流程都是完善的,这里我们不去过多的讨论 JS 动态库的相关内容,我们仅仅是用到动态下发的能力,核心思路是:

  • 利用 JS 动态库根据消息类型下发对应的 weex 卡片的 url
  • 利用 JS 动态库根据消息内容和消息类型来计算原生承载 weex 容器的 TableView 的 cell 宽高
  • 利用 JS 动态库下发需要加载 weex 卡片的消息类型
  • 利用 JS 动态库根据消息类型来判断当前的 cell 是否是 weex 卡片 cell

接入 JS 动态库

  • 我们在 JS 动态库中,暴露出获取 weex 卡片对应的 url 方法,入参是消息类型
  • 获取 weex 卡片所在的 cell 的宽高,入参是消息卡片内容和消息类型,JS 将卡片内容解析出来,进行高度、宽度的计算返回给原生。
  • 获取需要支持 weex 卡片的消息类型,原生根据这些类型进行容器 cell 的注册

性能

性能较好,在多个 weex 卡片消息交替出现的时候,为了避免达到性能瓶颈,因此首先考虑到利用原生 TableView / RecyclerView 的重用机制,拿 iOS 来举例,用 weex 的 url 和 TableView 的 identifier 做一个绑定,这样不必自己去维护 weex 卡片的重用和回收。第一版做出来的时候发现,这样在持续加载 weex 卡片的时候 WXSDKInstance 会不停的渲染调用 renderWithURL 方法,这样无疑是消耗比较大的性能去创建 weex 视图。因此我们在 weex 容器中做一个标记,记录上次该容器加载的 url,每次在加载 weex 卡片 cell 的时候去判断当前的 url 是否是上次记录的 url,如果是,就说明这个类型的 weex 卡片之前已经渲染过,WXSDKInstance 实例可以拿到原生传过来的数据进行 refreshInstance;否的话用 WXSDKInstance 进行 renderWithURL,这样可以节省一次 render 重新渲染,从而解决卡片太多,上下滑动的时候会出现短暂的空白现象。

整体流程

  • 消息通道连接的时候,weex 模块去拉取对应的注册的 weex 页面;初始化 JS 动态库,其实也就是拉取 JS 相关的逻辑配置
  • 后端下发消息数据,经过数据解析器将消息经过转化生成 layoutModel 缓存起来
  • 在消息卡片视图工厂类 contentFactory 中,将消息类型传到 JS 端来判断当前消息类型是否需要注册成 weex 卡片
  • 在获取卡片宽高、消息模型的卡片内容配置类 contentConfig 中,将消息类型、消息内容传到 JS 端,返回 weex 卡片宽高
  • 数据操作类去持有 LayoutModel 和 contentConfig 类,在 TableView 代理方法回调的时候,去加载对应的 weex 卡片和宽高,并且将 weex 卡片的 url 作为 identifier 重用标记
  • TableView 加载 weex 容器的 Cell 时传递消息类型到 JS 动态库,然后返回对应的 weex 卡片 url,然后根据 url 将消息卡片内容传递到 weex 容器端去进行渲染或者刷新

架构设计如下图所示:

消息动态化架构图

优化

目前整个流程已经上线,情况比较稳定,极大的提高了开发效率,增加 / 修改消息卡片做到完全动态化,但是目前我们的方案也有很多不足之处,比如我们期望更好的性能和更高的运行效率、更加动态化的事件处理能力、更加方便的开发体验,对此我们也做了更进一步的规划建设:

  • 组件创建、布局计算、数据绑定机制、从 JS 端获取数据做缓存的优化,提升性能
  • 规范事件点击回调处理,提供跳转商品详情、订单详情、原生特定页面 router 以及 webView 的事件处理
  • 拆分出动态化框架 SDK ,让其他需要用到的业务可以接入,比如一些活动页面

总结

以上是我们整个消息卡片动态化的整体方案,总结来讲就是在原生架构基础上配合 weex 技术栈、JS 动态下发,达到整个动态化的目的,可以算作一个轻量化的动态化方案,算是在动态化方面做一些尝试,也希望可以和广大开发同学一起交流。

参考链接:

本文转载自公众号有赞 coder(ID:youzan_coder)。

原文链接

https://mp.weixin.qq.com/s?__biz=MzAxOTY5MDMxNA==&mid=2455761163&idx=1&sn=32e318bdf0c847021abc5e621193172c&chksm=8c68772ebb1ffe38768f187645f73848879ed6bd7baf8f50a4b6e81556dc8b7ab56868d22579&scene=27#wechat_redirect

2020 年 8 月 08 日 14:05 1471

评论

发布
暂无评论
发现更多内容

就餐卡系统第一周作业「架构师训练营第 1 期」

天天向善

UML学习 架构学习

架构方法周总结第一周作业「架构师训练营第 1 期」

天天向善

UML学习 架构学习

聚焦2020云栖大会 边缘计算专场畅谈技术应用创新

巨侠说

从构建小系统到架构分布式大系统,Spring Boot2的精髓全在这里了

Java成神之路

Java 编程 程序员 面试 Spring Boot 2

洞爷湖-安静与灵动

刘旭东

摄影 摄影征文 洞爷湖 北海道

食堂就餐卡系统UML设计 - 架构师训练营第1周作业

netspecial

极客大学架构师训练营

Vue-防止重复点击指令

老菜鸟

Vue 指令

架构师训练营01周 -- 命题作业

骏马

极客大学架构师训练营

加速连接效率 阿里云推出5G消息使能平台MEP

巨侠说

Week 1 命题作业

阿泰

架构师训练营1期第1周:架构方法 - 总结

piercebn

极客大学架构师训练营

从开源协议到谷歌禁用华为、Docker实体清单事件

科技缪缪

GitHub Linux 开源 编程语言 开源许可证

机器学习在滴滴网络定位中的探索和实践

滴滴技术

人工智能 机器学习 滴滴技术

架构师训练营 - 第 1 周课后作业(1 期)

Pudding

期末大作业(一)

武鹏

多方计算——打开区块链应用新场景

CECBC区块链专委会

区块链 大数据

同事为进大厂天天刷Java面试题,面试却履败!究其原因竟是它在捣鬼。

Java成神之路

Java 程序员 数据结构 面试 算法

食堂就餐卡系统设计

一个节点

极客大学架构师训练营

并发+JVM+Redis+MySQL+分布式+微服务等及阿里等大厂最新面试问答

Java成神之路

Java redis spring 面试 JVM

阿里云发布边缘计算视频上云解决方案 为海量视图处理提供城市级云基础设施

巨侠说

边缘计算

不正经的计算机专业学生拍摄照片分享

王荣胜

摄影

架构训练营 - 第1周课后作业 - 学习总结

Pudding

云图说 | 通过Helm模板快速部署中间件应用

华为云开发者社区

容器 k8s

司法区块链破解互联网案件审判难

CECBC区块链专委会

区块链技术 不可篡改 法院

一周信创舆情观察(8.24~9.13)

统小信uos

程序员写个人技术博客的价值与意义

Java架构师迁哥

第一周总结

一个节点

极客大学架构师训练营

面试官:谈一下你对DDD的理解?我:马什么梅?

科技缪缪

Java 架构 编程语言 领域驱动设计 DDD

云栖大会CDN技术专场:如何构建企业级内容分发加速体验?

巨侠说

CDN

微服务 API 网关kong的爬坑之路

Dream

微服务网关 kong

拥抱K8S系列-08-通过rancher部署nginx应用

张无忌

nginx Kubernetes rancher

有赞移动消息卡片动态化方案实践-InfoQ