## Hybrid Go:去哪儿网 Hybrid 实践
文 / 林洋
随着科技的进步和用户需求的增加,近几年来智能设备类型越来越多,所搭载的操作系统也种类繁多。因此,在每个项目启动时,大家在考虑项目开发和维护成本的过程中,会着重分析所使用技术方案的跨平台适配性,以保证项目在多个平台上得以顺利运行,从而降低成本。而在诸多种方案中,Hybrid(混合开发)方案以极高的开发效率和足够低的开发成本受到广大开发者的青睐。Hybrid 方案究竟有哪些特性?它又是如何被广泛应用到各个应用场景?在本文中,我将基于去哪儿网在 Hybrid 领域的研究,为读者深入剖析 Hybrid
的起源、架构、实现及未来。
进入新纪元以来,互联网发展迅猛,尤其是随着移动浪潮和“互联网+”概念的兴起,各大厂商在移动设备和智能硬件上的需求迅速攀升。但与此同时,由于设备的繁多和定制化的差异,基于设备操作系统原生开发的成本逐渐升高,最明显的示例就是同样的功能逻辑在不同的设备上要用不同的编程语言、不同的代码结构去构建,学习成本、开发成本和维护成本大幅增加,因此引入一种开发更高效、成本更低的解决方案势在必行,而这个方案就是
Hybrid 混合开发。
去哪儿从2015年初,开始正式推行体系化的 Hybrid 方案。从基于 WebView 的
Hybrid方案(Hy),到深度定制的 React Native方案(YRN),去哪儿将 Hybrid 深入植入到移动 App 中,降低业务成本的同时,解决业务实际遇到的问题,为公司移动平台发展注入了新鲜血液。
### 什么是 Hybrid
Hybrid 这个名词越来越多地出现在人们的视野中是源于混合动力汽车(Hybrid Electrical Vehicle,简称 HEV),由汽油和电池一起提供动力,结合了油车续航长、补充快和电车清洁、低耗能的两方面优势,达到了发动机和电动机的最佳匹配。在互联网技术中的,Hybrid 方案也具有同样的特性——结合两种混合技术的优势,例如 Web 技术的跨平台、快速部署与原生 Native 的功能、性能相结合。
图1 推翻“三座大山”,Hybrid 为开发者减负
大多时候,圈内人谈论到 Hybrid 时,一般是指移动端内嵌 WebView 的开发方案。实则不然,广泛地讲,包括 React Native 方案、各厂商的小程序方案都属于移动端
Hybrid 解决方案的范畴;而 NW.js、Electron 等则属于 PC 端 Hybrid 解决方案。对于智能设备,大多基于 Android 系统,因此,智能设备上使用的 Hybrid 方案与移动端基本一致。
具体来看,移动端混合开发中的 Hybrid 方案,主要有三种形式,如图2所示。
图2 Hybrid 方案的三种主要方式
- 基于 Web 的解决方案,例如:Cordova、微信浏览器、各公司的 Hybrid 方案;
- 非基于 Web UI 但业务逻辑基于 JavaScript 的解决方案,例如:React Native;
- 基于 Web 且 UI 层与逻辑层分离的解决方案,此类型的代表是微信小程序,将UI展现逻辑和业务逻辑分离到多个 JavaScript Context 里,提高运行效率,效果很好。
去哪儿现在使用的形式既有第一种,也有第二种,就是前文所说的基于 WebView 的
Hybrid 方案和深度定制的 React Native 方案。而第三种方式,我们正在调研中,也是团队今后工作的一个重点。
### Hybrid 的优劣势
通过对去哪儿所采用的方案的大概介绍,大家或许有一个疑问:为什么在维护两个不同方案的同时,继续第三种方案的探索?原因是,不同的方案之间有很大的差异性,同时它们也适用于不同的业务场景。为了更利于理清每个方案间的差异性,先了解 Hybrid 与其他方案的异同点是非常有必要的。
首先,既然我们将移动 Hybrid 方案暂时限定在移动端,那么可以对移动端三个最常见的形式 Web App、Hybrid App 和 Native App,进行比较。
表1中列出了在几个重点方面的比较。
表1 Web App、Hybrid App 和 Native App 之间的对比
其中,体验包括“原生能力”和“性能”两个方面。原生能力指调用联系人信息、接收 Push 信息等,而性能主要指渲染效果和 App 使用时候的流畅程度。
在这里,存在一个误区就是:Hybrid 最大的优势在于 Web(JavaScript)的跨平台特性。其实并不然,虽然 Web(JavaScript)的跨平台特性是 Hybrid 能取得如此大成功的前提,但是跨平台特性只是减少了一部分开发成本,并没有解决真正的痛点。而对于一个企业来说,真正的痛点是更新速度。不论是 iOS 还是 Android,客户端发布后,需要通过平台方的审核,审核通过后,用户是否会及时更新也是一个很大的问题。尤其是 iOS,绕开 App Store,在 App 内部实现一套基于 Native 的更新机制,是被禁止的,甚至近期连 JSPatch(使用 JavaScript 调用 Objective-C 的原生接口,从而动态植入代码来替换旧代码,以实现修复线上 Bug)也被 Apple 明令禁止了。
因此,Hybrid 方案的热更新特性成为了它的最大优势。在降低开发成本的同时,让用户更新到新版本的时间变短,是非常重要的。
而 Hybrid 方案的劣势呢?那就是性能,包括初始化的效率(首屏时间)和展现流畅度(主要体现在列表视图中),都和 Native 有一定的差距。
去哪儿先推出的是基于 WebView 的 Hybrid 方案,解决了业务快速迭代和 App 的体积问题,被业务线大幅度使用;但是,当使用场景无限增多后,性能问题被暴露出来,主要体验在复杂的无限加载的列表场景,在一些 Android 设备上,列表滚动有卡顿现象,体验比较差。正当此时,Facebook 提出了 React Native,一个基于 Native UI 的
Hybrid 方案,将 UI 展现交给 Naive 来实现,从而提高展现效率。因此,我们基于
React Native 针对去哪儿客户端的实际情况进行了深度定制和大幅度优化(原始
React Native 的问题实在太多)。
原本以为“银弹”即将诞生,但是又遇到了一个新问题“扩展性和性能的矛盾”。如果要追求性能,那么 UI 逻辑需要尽可能多的交给 Native,这样一来,业务方 JavaScript 对
UI 控制性将会降低,业务不能“想加什么就加什么“,反之亦然。同时,React Native
在开发维护成本和测试成本上,要高于基于 WebView 的 Hybrid 方案,也是一个比较棘手的问题。
虽然这两种非“银弹”的方案已经能满足绝大部分开发和业务场景,但是“完美”是有必要去追求的,更好的方案或许正在路上。
### Hybrid 要点
对于 Hybrid 方案,有很多需要注意的要点,下面选择其中比较重要的三点进行简要说明。
#### 客户端架构
上文中,提到了 Hybrid 方案主要的三种形式。这三种形式客户端部分的简要架构图如图3所示(以 Cordova、React-Native、微信小程序为代表)。
图3 三种 Hybrid 方案中客户端部分的简要架构图
从图中可以看到,不论是哪种架构,都是通过 Bridge 调用 Native API,从而给
JavaScript 端提供 Native 原生能力。
而不同是,类 Cordova 的方案的业务逻辑和 UI 逻辑在同一个 WebView 里,尤其在多 WebView 的方式下,会给设备内存带来很大的压力,导致掉帧等状况。与之相反,React Native 和微信小程序类的方案,将业务逻辑的 JavaScript 与 UI 逻辑分离,放到单独的 Context 中执行。这点很像高性能的 iOS 或 Android 架构,分离业务层和 UI 层,并将 UI 层放置到主线程,从而提高 UI 层的效率,最终提升用户的体验。
当然,业务逻辑与 UI 分离的设计也存在一定的问题待解决。
JSCore 与 Native UI 或者 Web UI 之间也是通过 Native 的 API 进行通信的,也就是有一个隐形的“Bridge”在工作。同时,与 UI 层通信的频率远远大于直接调用 Native 功能的频率,因此这个隐形的“Bridge”的通信效率可能会成为这类方案的一个瓶颈,尤其在 React Native 类的方案中。
#### Bridge
Bridge 简单来讲,主要是给 JavaScript 提供调用 Native 功能的接口,让混合开发中的“前端部分”可以方便地使用地理位置、摄像头甚至支付等 Native 功能。
既然是“简单来讲”,那么 Bridge 的用途肯定不只“调用 Native 功能”这么简单宽泛。实际上,Bridge 是 Native 和非 Native 之间的桥梁,它的核心是构建 Native 和非 Native 间消息通信的通道,而且是双向通信的通道,见图4所示。
图4 作为桥梁,Bridge 提供双向通信通道
- JavaScript 向 Native 发送消息:调用相关功能、通知 Native 当前
JavaScript 的相关状态等。
- Native 向 JavaScript 发送消息:回溯调用结果、消息推送、通知 JavaScript 当前 Native 的状态等。
而 Bridge 是如何实现的呢?JavaScript 是运行在一个单独的 JavaScript Context 中(例如,WebView 的 Webkit 引擎、JSCore)。由于这些 Context 与原生运行环境的天然隔离,我们可以将这种情况与 RPC(Remote Procedure Call,远程过程调用)通信进行类比,将 Native 与 JavaScript 的每次互相调用看做一次 RPC 调用。如此一来我们可以按照通常的 RPC 方式来进行设计和实现,见图5所示。
图5 Bridge 的设计和实现过程
在 Bridge 的设计中,可以把 JavaScript 端看做 RPC 的客户端,把 Native 端看做 RPC 的服务器端,从而 Bridge 要实现的主要逻辑就出现了:通信调用(Native 与JavaScript 通信)和句柄解析调用。
#### 热更新机制
对于一个 Hybrid 方案,热更新机制是必不可少的。热更新的前提是离线机制,将业务资源从服务器端下载到本地,并从本地加载相应资源,从而保证了业务资源的加载速率,保证了体验的顺畅,见图6所示。而热更新机制则是为了保证离线的资源尽可能早、尽可能快地更新到用户端,让用户更早地使用到最新的产品功能。
图6 静态资源的热更新过程
在这里,重点要说明的是两个关键点:检查下载更新和替换资源的时机。与 HTML5 提供的 AppCache 以单个页面为单位不同,Hybrid 项目热更新的单位一般是模块,并且模块间可能存在依赖关系,因此检查下载更新和替换资源的时机,以模块为维度进行分析。不论是模块的全量包还是差分包,都有一定的大小,这样的话,下载更新包都会需要消耗用户的一些网络流量,所以也要考虑到用户的网络环境。综合上面的分析,检查下载更新的时机要保证离线包更新率的同时,尽量少的消耗用户的3G/4G网络流量,而替换资源的时机要保证模块的正常使用。
对于,检查下载更新的时机可以有以下选择:
- 在 WIFI 下启动 App,全量更新(更新这个 App 所需的所有离线包);
- 在进入相应模块时,检查并下载更新包。
其中 App 启动时更新逻辑的流程图,如图7所示。
图7 App 启动时更新逻辑流程图
而对于替换资源的时机,一般是在此模块不被使用时,例如:项目启动、回到首页、App 后台运行。
当然,更新不仅仅是设计问题,更注重的其实是效果,其中离线包的到达率是一个很重要的指标。当然,一味的追求到达率,在离线包下载完成之前,不进行加载这样的方式,虽然保证了到达率,但是也影响了资源的加载速度。如何找到一个适中的方案是非常关键的。经过多年的探索和实践,去哪儿根据业务的需求和实际的线上效果,最终总结出一套比较完善的热更新逻辑,简单的流程图见图8所示。
图8 更为完善的热更新逻辑流程图
从图8中我们可以看到业务方可以根据业务实时性的情况,来配置是否强制更新。在更新过程中,有一个“2秒”的阈值,来保证加载新离线资源的效率(去哪儿96%的离线包可以在2秒之内下载完成)。
上面从架构、Bridge 和热更新机制三个方面介绍了 Hybrid 方案的要点,希望能帮助大家对 Hybrid 有更深一步的理解。
### Hybrid 的未来
当前移动端持续火热,再加上“互联网+”的智能设备层出不穷,Hybrid 方案的可应用场景越来越多。不论是 Cordova 类的基于 Webview 的方案,还是 React Native 类与
Native 结合紧密的方案,更不用说微信小程序类的新颖方案,都有各自的优劣势,每个不同的方案都有自己最适应的应用场景。
总之,Hybrid 方案的出现不是为了替代谁,更多的是为了“ Native 与 Web 技术彼此间的美好”,让 Native 和 Web 技术更关注自身的优势,最终可以形成一个辅助业务团队进行快速开发、高速迭代,而且降低开发、更新成本的一套实用、务实的一体化渐进式解决方案。
对于未来,随着各种技术的不断发展,Hybrid 方案所遇到的瓶颈将会被不断攻破,而它所具有的更新快、跨平台、开发成本低的特性,将使它在众多用户端解决方案中脱颖而出。