当下,移动动态化已经成为各大公司都回避不了的问题,产品的快速迭代对技术提出了更高的要求,而移动端的动态化方案也是层出不穷:Hypid、结构化 Native View、React Native、Weex,什么样的方案才是适合自己团队的呢?本文将分享饿了么蜂鸟团队在过去两年多业务快速增长过程中,移动动态化方面的实践和探索。

什么是移动动态化?

移动指的是移动端,包括安卓、iOS。动态化则是动态部署和逻辑下发到客户端的能力。移动动态最好的状态就是让移动应用和 Web 一样,想发就发!

为什么移动端要强调动态化的能力?

原因有如下三大点:

  •     业务迭代太快。当下大部分团队都是敏捷开发的模式,即使两周做一次迭代,产品周期还是会觉得长,有些应用不能及时上线。

  •     应用市场审核慢。安卓基本当天发应用市场,当天就能够有更新。但 iOS 需要约 3-4 天来审核。假设有些功能需要定时上线,iOS 审核时间必须要考虑进去。

  •     用户升级周期长。统计表明,每一个安卓版本发布,一周内会有 70% 的用户更新,一个月其余用户才能陆续完成更新。

移动动态化方案共性,有如下三点:

  •     跨平台。

  •     布局。约定 DSL,保证渲染性能。

  •     逻辑。Android 和 iOS 必须共用解释器。

蜂鸟团队的现状与业务特点

蜂鸟团队现状

蜂鸟团队于 2014 年成立,初衷是为了承接饿了么的物流业务。随着时间推移,订单量从每日几千单到百万单,配速员也达到百万数量,服务品类涉及外卖、商超、鲜花、蛋糕、文件等,蜂鸟提供全时段配送,配送服务覆盖全国 1200 多个城市。

蜂鸟团队的业务特点

蜂鸟团队的业务主要有离散性和突发性两大特点,如下图:

从业务曲线可以看到两个很明显的波峰,这是午、晚用餐时间。同时,如果运营方面配置一些活动,会导致这两个波峰徒增。所以,动态方案要想把这两个时间段服务好,必须要考虑流量陡增下的性能压力。

蜂鸟团队的技术特点和挑战

蜂鸟团队的技术特点和挑战,我主要分享重度依赖、网络环境复杂、重度使用和 28 定律这四个方面。

重度依赖

当前蜂鸟有众包、团队和送送三部分业务,右侧是一些功能展示,如下图:

这样的工具型应用,需要对 APP 有更强的控制、监控等能力。必要时还要做到强制更新。

对应到动态方案的话,控制能力就需要动态方案必须具备动态降级的能力、监控能力,实时的性能监控和业务埋点监控。强制更新方面,动态方案必须做到用户无感知的热更新。

网络环境复杂

饿了么小哥,每天穿梭在大街小巷、地下商超,他们的网络环境非常不稳定。据统计,有近 25% 的用户请求还来自非 4G 环境。

整体来说的网络环境复杂、信号差和 DNS 污染,那么动态方案就要解决 DNS 拦截、弱网环境下资源下发等问题。

重度使用

无论是下雨、下雪,还是发洪水大家都会叫饿了么。

配送员在高峰期的运动曲线,如下图:

面对这样争分夺秒的准时达压力,如果动态方案不给力,会导致应用出现崩溃或卡顿,骑手必定不会有好的体验,甚至影响送餐时间。所以我们的动态方案一定要保证性能和稳定性。

28定律

相信很多公司的应用都符合类似 28 定律,蜂鸟也不例外。

如下图,蜂鸟的 28 定律

可以从图中看出,大部分骑手日常使用的主流层面,可以采用 Native 来开发,这部分重度使用的占比约 20%,其余 80% 的功能都可以考虑动态化方案(H5)。

蜂鸟团队的动态化架构演进

蜂鸟的动态方案经过 Hypid、React Native 和 Weex 三个主要阶段。

第一阶段:Hypid

在 Hypid 方案上,以 H5 的动态性为基础,通过 Jspidge 做桥梁,与 Native 进行通信,之后通过 URL Router 进行跳转,架构如下图:

这套动态方案的优点显而易见,这里主要介绍开发效率、更新体验和跨平台三方面:

  •     开发效率。Web 经过多年的应用实践,已经拥有完整的开发流程和开发工具,开发一个 H5 页面非常快速。开发效率这一因素不能忽略,因为初期产品的想法和落地速度会直接影响产品的命运。

  •     如蜂鸟送送,初期没有原生的资源去支撑,就用原生包壳,内部全部用 H5,这样的情况坚持了两月左右,为蜂鸟送送前期的方案验证做了很大的贡献。

  •     更新体验。因 H5 和原生耦合只有扩展的 Native API,只要把这些 API 维护足够全,开发的业务功能就可以在完全不用更新 APK 的情况下,做到热更新。且用户下一次打开应用是最新的,这和 Native 的升级体验相比简直是一天一地。

  • 跨平台。之前安卓和 iOS 代码需要开发两次,现在一个功能决定用 H5 后,由一个工程师来开发一套代码即可。

这套动态方案很大的缺点就是用户体验差,当用 H5 做一些复杂的功能或动画时,可能会卡顿的和 PPT 一样。因为 H5 的体验问题,蜂鸟的原则是经常更新的且功能不复杂的页面会选择用 H5。

第二阶段:React Native

这个动态方案完全脱离了以 H5 为基础的 Hypid 方案,通过自定义 DSL 将 UI 渲染成原生控件,这样一来, RN 的页面就保证了原生的体验和 Web 的效率。

除了上一点,还有组件化开发、复用率高、Android 和 iOS 95% 的代码共用和测试效率高等优点。

鉴于这些优点,蜂鸟在 React Native 上做了很多事情,如 Crash 优化、基础控件沉淀、Bundle+ 图片热更新、首屏加载优化和 Redux 单项数据流等。

正当享受 React Native 带来的开发体验和应用体验提升时,蜂鸟遇到 RN 的一些痛点,如 ScrollView 性能、Bundle 包过大、很多优化都需要修改源码和 peaking change 等。

第三阶段:WEEX

面对如上这些痛点,不知如何应对时,WEEX 来了。官方宣传的轻量、可扩展和高性能等特点,让蜂鸟团队眼前一亮。

经深入研究后,蜂鸟发现 WEEX 和 React Native 如出一辙,那么为什么要选择类似的方案呢?

我们队 WEEX 和 React Native 两者基于 JS 引擎、语法、数据流、性能、开发体验及热更新等维度进行了对比。

如下图,是 WEEX 和 React Native JS 引擎对比

React Native 在安卓和 iOS 使用的都是 JsCore,WEEX 在安卓端使用的是 UC 精简版 V8。如上图中的图表可以看出,V8 相比 JsCore 要胜一筹。

WEEX 和 React Native 语法对比。语法方面,React Native 使用的是 React,WEEX 使用的是 Vue。虽然两套方案都实现了如响应式,组件化、状态管理等功能。

如下图,是两者简单 Demo 的实践:

实践发现,WEEX 相比 React Native 要优雅一些,是因为 Vue 有很多自定义标签,当在做一些 UI 和逻辑交杂在一起时,会让代码简洁很多。

 WEEX 和 React Native 的数据流对比,React Native 使用 Redux,而 WEEX 使用 Vuex,不是 WEEX 不能使用 Redux,而是 Vuex 更适合 WEEX。

如下图,是两者的数据流,大同小异:

但 Vuex 在实现一些计算属性时,能在更细的颗粒度去更新 UI,而 Redux 只能实现到组件的级别,这样的点很多的话会带来性能上的差异。

如下图,是 WEEX 和 React Native 的性能对比,左侧是 WEEX 官方给出的与 React Native 在性能方面的对比图:

在渲染时间和内存占用方面 WEEX 要优于 React Native,在 CPU 占用方面两者相差不大,FPS 上 WEEX 要稍逊于 React Native。

在 ListView Android 方面,React Native 目前采用 ScrollView,WEEX 使用 Recyclerview 实现,性能稍好。

同时 WEEX 在增强开发、指定线程、首屏渲染和性能监控等方面也做了优化。

如下图,是 WEEX 和 React Native 的开发体验对比

和 React Native 相比,WEEX 在打包、监控性能、跨平台等方面都有一定优势。总体来说,React Native 更像是一个技术框架,WEEX 更像是一个业务框架。

如下图,是 WEEX 和 React Native 的热更新对比:

React Native 与 WEEX 官方都表示支持热更新,但他们的实现方式不同。在 React Native 上可通过把图片打包下发到本地来实现更新。

WEEX 有两个方法,一是选择本地资源加载,二是像网页一样直接加载页面。

如下图,是 React Native 与 WEEX 的对比总结

React Native 更像一个先驱者,拥有超强的社区人气,但也因开源社区维护代码的原因处于一个野蛮生长的状态。而 WEEX 是站在 React Native 的肩膀上,做了各种微创新,实现更多贴心的小细节。

基于 WEEX 性能、稳定性等方面都比 React Native 高,蜂鸟决定把动态化方案往 WEEX 上迁移,虽然它现在还有不足,有些轮子还是要自己去做。

蜂鸟团队 WEEX 实践

凭借之前 React Native 相关的实践经验,基于 WEEX 做了一套更完整的动态方案。涉及以下几个方面,如下图:

统一的pidge 

在 Android & iOS 端,约定相同的方法名、参数,在 JS 层抹平平台差异以及统一分类管理暴露给业务的 API。

把这样的统一 pidge 方案提供给业务部门,他们只需关心暴露的 API,而不需要关心下一层平台的兼容,大大提升开发效率。

加载更新策略

加载更新方面,我们约定了一套自有协议,有 Page、URL 和 Tag,通过封装的 Router,就可以做到页面级的跳转。

这样一来,我们很轻松地做到了页面的跳转、解耦和页面的降级。当页面出现问题,只需要把 URL 改成降级之后的 H5 页面下发即可,用户触及到的就是修复之后的 H5 页面了。

如下图,是预加载策略

当 H5 页面下发到客户端之后,会对本地资源进行检查,如果有 JS 文件,就忽略,没有的话就把页面下载。当用户打开页面,再去看本地,存在资源的话直接加载,不存在的话就即时下载再运行,与传统的 Web 流程相似。

性能监控

性能监控用来判断线上服务是否正常,是整套方案最重要的部分。

WEEX 可以很方便地将所有的参数全部拿到且通过反射拿到所有的性能数据传到云端。

基于这些数据,我们就可以知道线上有了哪些页面,它的渲染是否有问题。基于这些问题,就可做相应的优化。

如下图,是线上的数据情况

监控三个指标,分别是 JS 引擎的初始化时间、页面打开时间和网络时间。因大部分 WEEX 页面都是业务,所以说业务埋点必不可少。饿了么也实现了一套框架,将业务埋点传给服务端,然后方便产品去制定一些产品方面的策略。

JS 的错误统计

可以捕捉 JS 端抛出的错误,如果所处团队是前端主导,可传给前端。如果是 Native 主导,可通过搜集平台将这些崩溃上传,在后台看到这些错误之后,找到相应的代码去修复。

Native 的错误

有了 JS 错误,Native 错误也不能忽略。

如下图,是 WEEX 动态方案上线一周之后线上抛的错误:

从图中可以看到都是个位数,这一点其实当时也很惊讶,WEEX 确实做得很稳定,这一点超出预料。

共用组件和 API

之前蜂鸟在 React Native 上面的一些实践,积累了一些很常用的组件和 API。WEEX 和 React Native 都是使用 JS 实现,所以我们很方便的将 RN 的控件转化为 WEEX 控件。

如下图,是实现的组件和 API,几乎可以满足中小团队的日常使用:

调试工具

这方面 WEEX 做的很贴心,虽然没有整合到整个初始化的项目中,但开源了几个库,可把代码拷贝到业务中进行使用。

WEEX 还可支持 Debug 模式显示调试工具、支持 hot reload、方便的查看性能指标和 Shell 脚本一键打包等功能。

综上所述,基于这些维度实现的框架,可以方便的让业务来使用。

如下,是饿了么和蜂鸟用 WEEX 实现的两个页面:

饿了么的第二个发现页面,就是基于 WEEX。蜂鸟 APP 可能大家接触不到,上图是当前通知的活动界面,还有大量的新功能正在接入。

如果你正在考虑 WEEX 与 React Native 方案,或是正在接入 React Native。看到这篇文章,你可以去调研以下 WEEX 方案,可能你会有另一种选择。

以上内容根据许锦洋老师在 WOTA2017 “移动端架构演进”专场的演讲内容整理。

负责饿了么蜂鸟 APP 的架构、研发等工作。拥有饿了么商家、风行者、蜂鸟众包等多款 APP 开发工作经历,并从 0 开始架构和开发了整个蜂鸟团队 APP。目前关注的技术方向为移动跨平台技术方案、移动端架构、移动端性能优化等。