58App-Android端的动态化框架实践与思考
导语
针对58业务需求特点,实现多端的快速开发,多套跨平台框架,业务跨App迁移。
背景
58业务需求特点:多端的快速开发,多套跨平台框架,业务跨App迁移。为了满足此业务要求,整体技术框架的实现手段有:
1、业务层:转译框架,如WubaRN-M,京东-taro-react标准,MPVue-vue规划,Wepy-类vue规范,滴滴-Mpx-小程序语法等等。
2、跨平台框架层:Hybrid,ReactNative,小程序,Flutter等等。
3、基础能力层:统一Plugin。
整体技术框架图
58APP动态化实践
每种技术都有其优缺点,无法做到完全统一,在实际的App里,一般都是多套框架并存,不同的业务采用不同的技术方案,58App里主要有三种技术方案:
WubaRN:基于ReactNative的二次封装,主要用于追求动态和较高体验要求的需求。
Hybrid:JS-Native框架,主要用于运营活动等需求。
阿里的Tangram框架:用于列表等Native页面的布局动态化。
1、WubaRN
WubaRN框架图
抹平UI组件的平台差异,扩展Native能力。 统一技术栈,提供模板工程。
减少平台支持版本,保留armeabi-v7a;统一Okhttp与Fresco。 通过AOP修改字符码,解决原生Bug。
Bundle拆分:Common Bundle内置,Business Bundle动态下发。 分步加载:优化加载Common Bundle,具体业务再加载Business Bundle。
ReactNative实现框架图
a) 三个线程
UI Thread:Native的UI渲染。 Shadow Thread:yoga引擎,基于flexbox的语法糖转换为各端的扁平化框架。 Javascript Thread:React执行环境,业务逻辑与diff操作执行环境。
减少平台支持版本,保留armeabi-v7a;统一Okhttp与Fresco。 通过AOP修改字符码,解决原生Bug。
列表滑动白屏:快速滑动,通信量大,过渡依赖Bridge。
转输大数据慢:如图像的base64字符串信息。
无线同步通信:通信都是异步。
Javascritp Thread帧率低:Javascript解释执行,同时需要执行业务逻辑与diff操作,在低端手机里,快速滑动时,掉帧严重。
1.4、ReactNative的最新版本也在重新整体底层实现:
a) Fabric
将Native API直接暴露给JavaScript,不通过bridge。
允许 UI 线程与JS线程同步。
b) Fiber:利用requestIdleCallback(),实现动画优先。
c) 使用RecyclerView替换FlatList,实现ItemView的复用。
自动化用例管理平台,不能很好管理自动化用例,影响测试效率。
2.1、其在性能与灵活性上取了一个折中解决方案:
a) 设计原则:牺牲灵活性的情况下,追求极致性能。
b) 切入点
Native列表高性的同时,缺少灵活性。
动态框架的内存与滑动控制的性能瓶颈。
c) 目标:通过构建页面结构化描述,实现页面可运营的目的。
2.2、其主要的应用场景:
a) 常规业务:如业务稳定的列表等
需求较稳定,对性能与稳定性有很高的要求。
对局部样式有动态化要求,如标签等等。
b) 基础业务:如首页
需求稳定,对性能与稳定性有很高的要求。
对局部样式有动态化要求,如推荐样式。
2.3、其高性能的原因:
基于Native的列表实现的基础上,解决灵活性,如RecyclerView。
页面渲染:大量的计算工作在VM中完成,并缓存在VM组成的树形结构里。
回收和复用:基于组件与控件实现回收复用。
模板管理后台:负责发布、更新(版本、平台、组件版本、生效优先级)。 页面生成工具(类似索尔平台)。
其他动态化框架分析
小程序:微信小程序,百度小程序等等。 全包型:Flutter,Qt。 转译框架:taro,MPVue,Wepy,Mpx等等。
1.1、通过对百度小程序的已开源的源码分析,其整体框架如上图所示:
a) 逻辑层
小程序Api:App(),Page(),布局标签。
App():创建App对象。
Page():存储在Map中,页面显示时,创建Page对象。
b) 渲染层
MVVM框架San渲染。
编译期间,小程序标签转化为San的标签。
Page()对应San的Page组件,Template为Swan.xml转译的内容。
c) 交互
渲染层接收用户的交互事件,由统一的函数处理后,通过消息总线传递到逻辑层的Page对象,再调用对应的函数。
逻辑层依据用户操作,执行业务操作,修改data数据,通过消息总线传递到渲染层的组件里,San.Page组件会自动更新界面。
1.2、不管是逻辑层与渲染层,其内部实现都还是通过H5来实现,其提升性能的思路:
把逻辑层与渲染层分离。
异步请求都由native来执行。
编译期转换标签。
1.3、受制当前的实现机制,有一些短板:
a) 无法内嵌Native的View。
b) 通信机制:异步且序列化传递数据。
传递大数据较慢。
setState()过于频繁时,影响性能。
2、全包型-Flutter
2.1、Flutter借鉴了ReactNative的设计思路,采用响应式编程,同时实现统一跨平台样式,高性能。
Debug为字节码,Release为机器码。
不依赖OEMWidgets。
不依赖Bridge。
2.2、开发效率:
声明式布局,一切都是Widget。
热加载(hotreload)。
不依赖OEM,基于Skia,统一UI。
2.3、真正体验后:
a) 感受:
开发调试非常的快,比InstantRun强。
依赖库管理强,Plugin库。
MVVM框架,声明式布局,便于组件化。
代码精简,相比Java。
b) 不足:
iOS不支持热更新(思路:Dart转Javascript)。
生态不完善,缺少必须的基础能力。
i) 渐变Button,图片Button。
ii) 崩溃日志收集。
iii) 基础Plugin:相册,授权,视频等等。
定制开发学习成本高(RenderObject, CostomPaintObject)。
3、转译框架-Taro
Taro框架
3.1、Taro的设计目标:
a) 用React写小程序,实现“工业化”。
PostCSS,Sass,Less
NPM 支持
ES6/ES7 语法糖
TypeScript的强类型约束
React 的组件开发
Redux 的状态管理
b) 多端,统一语法。
多端转换:运行时和编译时。
抹平多端差异:基础Api与基础组件。
Taro框架
Taro框架
3.2、Taro是类React语法:以小程序为标准,对React语法删减,有如下限制:
a) JSX的限制。
render()之外不支持jsx
map 中不支持if表达式
只支持Array.map
props中不支持匿名函数
props中不支持对象展开符
props不支持JSX 元素
b) 不支持无状态组件,都必须使用class定义。
3.3、转译框架带来的问题:
问题定位难:多一层转译。
基础API与基础组件的维护。
总结、展望及规划
现阶段没有真正能满足所有需求的框架,处于混合时期:依据不同的场景,采用不同的框架。
1、移动端:
首页:布局动态化。
列表,详情:Web技术型,如ReactNative。
活动:Hybrid,小程序。
2、多端:编译框架,如Taro
业务要求与技术要求
从长远看,真正有可能实现统一的框架:Flutter(全包型)。
最关键的还是:持续关注,持续学习。
作者简介
刘阳,2013年加入58,现主要负责58App的Android端产品,持续关注大前端的框架技术,有兴趣的同学多交流。
END
阅读推荐
API管理平台之系统设计篇
开源|Magpie可视化圈选埋点实践
开源|Magpie:组件库详解
详解站点压测利器——nGrinder
开源|Magpie:Magpie在安居客有料业务的落地实践