写点什么

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

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:051832

评论

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

架构师训练营第二次作业

月殇

极客大学架构师训练营

第 2 周 框架设计 腐败的代码

Pyr0man1ac

架构师训练营第二周总结

月殇

极客大学架构师训练营

架構師訓練營 week2 總結

ilake

第二周总结

赵孔磊

C语言与C++学习路线

C语言与CPP编程

c++ 编程语言 C语言

架构师训练营 1 期第 2 周:框架设计

Wee权

【第二周】课后作业

云龙

极客大学架构师训练营

面向对象设计原则及框架案例

garlic

极客大学架构师训练营

极客时间架构 1 期:第 2 周框架设计 - 命题作业

Null

架构师训练营第二周作业

赵孔磊

作业一

泡泡

学习总结1

Wee权

Week 2 命题作业及总结

阿泰

依赖倒置原则和接口隔离原则

garlic

极客大学架构师训练营

极客时间架构1期:第2周框架设计-学习总结

Null

数据结构之堆栈

C语言与CPP编程

c++ 数据结构 堆栈 C语言 数据结构与算法

架構師訓練營 week2 作業

ilake

极客大学架构师训练营

华为18级工程师十年之作,整整3625页互联网大厂面试题合集

云流

学习 程序员 Java 面试 架构师技能

架构师训练营第 1 期第二周课后练习题

郑凯元

极客大学架构师训练营

Week 2 总结

黄立

训练营第二周作业 1

仲夏

训练营第二周作业 2

仲夏

用户故事信息过多或过少带来的问题

Bruce Talk

敏捷 Agile 用户故事 UserStory

【第二周】框架设计

云龙

极客大学架构师训练营

极客大学 - 架构师训练营第一期 - 第二周作业

Black Eyed Peter

极客大学架构师训练营

第二周总结

fmouse

极客大学架构师训练营

依赖倒置原则(DIP)

leo

极客大学架构师训练营

面向对象设计原则

leo

极客大学架构师训练营

第二周作业

fmouse

极客大学架构师训练营

数据结构之线性表

C语言与CPP编程

c++ 数据结构 C语言 线性表 数据结构与算法

技术为帆,纵横四海- Lazada技术东南亚探索和成长之旅

技术为帆,纵横四海- Lazada技术东南亚探索和成长之旅

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