Hybrid App 从入门到实践开发
现有混合方案
Hybrid App,俗称混合应用,即混合了 Native 技术 与 Web 技术 进行开发的移动应用。现在比较流行的混合方案主要有三种,主要是在 UI 渲染机制上的不同:
基于 WebView UI 的基础方案,市面上大部分主流 App 都有采用,例如微信 JS-SDK,通过 JSBridge 完成 H5 与 Native 的双向通讯,从而赋予 H5 一定程度的原生能力。
基于 Native UI 的方案,例如 React-Native、Weex。在赋予 H5 原生 API 能力的基础上,进一步通过 JSBridge 将 js 解析成的虚拟节点树(Virtual DOM)传递到 Native 并使用原生渲染。
另外还有近期比较流行的小程序方案,也是通过更加定制化的 JSBridge,并使用双 WebView 双线程的模式隔离了 JS 逻辑与 UI 渲染,形成了特殊的开发模式,加强了 H5 与 Native 混合程度,提高了页面性能及开发体验。
以上的三种方案,其实同样都是基于 JSBridge 完成的通讯层,第二三种方案,其实可以看做是在方案一的基础上,继续通过不同的新技术进一步提高了应用的混合程度。
Hybrid 技术原理
Hybrid App 的本质,其实是在原生的 App 中,使用 WebView 作为容器直接承载 Web 页面。因此,最核心的点就是 Native 端 与 H5 端 之间的双向通讯层,其实这里也可以理解为我们需要一套跨语言通讯方案,来完成 Native(Java/Objective-c/…) 与 JavaScript 的通讯。这个方案就是我们所说的 JSBridge,而实现的关键,便是作为容器的 WebView,一切的原理都是基于 WebView 的机制。
(一)JavaScript 通知 Native
基于 WebView 的机制和开放的 API, 实现这个功能有三种常见的方案:
- API 注入,原理其实就是 Native 获取 JavaScript 环境上下文,并直接在上面挂载对象或者方法,使 js 可以直接调用,Android 与 IOS 分别拥有对应的挂载方式
- WebView 中的 prompt/console/alert 拦截 ,通常使用 prompt,因为这个方法在前端中使用频率低,比较不会出现冲突
- WebView URL Scheme 跳转拦截
第二三种机制的原理是类似的,都是通过对 WebView 信息冒泡传递的拦截,从而达到通讯的,接下来我们主要从 原理-定制协议-拦截协议-参数传递-回调机制 5 个方面详细阐述下第三种方案 – URL 拦截方案。
1、实现原理
在 WebView 中发出的网络请求,客户端都能进行监听和捕获。
2、协议的定制
我们需要制定一套 URL Scheme 规则,通常我们的请求会带有对应的协议开头,例如常见的 https://xxx.com 或者 file://1.jpg,代表着不同的含义。我们这里可以将协议类型的请求定制为:
1 | xxcommand://xxxx?param1=1¶m2=2 |
不同的协议头代表着不同的含义,这样便能清楚知道每个协议的适用范围。
(2) 这里不要使用 location.href 发送,因为其自身机制有个问题是同时并发多次请求会被合并成为一次,导致协议被忽略,而并发协议其实是非常常见的功能。我们会使用创建 iframe 发送请求的方式。
(3) 通常考虑到安全性,需要在客户端中设置域名白名单或者限制,避免公司内部业务协议被第三方直接调用。
3、协议的拦截
客户端可以通过 API 对 WebView 发出的请求进行拦截:
IOS:shouldStartLoadWithRequest
Android:shouldOverrideUrlLoading
当解析到请求 URL 头为制定的协议时,便不发起对应的资源请求,而是解析参数,并进行相关功能或者方法的调用,完成协议功能的映射。
4、协议回调
由于协议的本质其实是发送请求,这属于一个异步的过程,因此我们便需要处理对应的回调机制。这里我们采用的方式是 JS 的事件系统,这里我们会用到 window.addEventListener 和 window.dispatchEvent 这两个基础 API。
1.发送协议时,通过协议的唯一标识注册自定义事件,并将回调绑定到对应的事件上 。
2.客户端完成对应的功能后,调用 Bridge 的 dispatch API,直接携带 data 触发该协议的自定义事件
5、参数传递方式
由于 WebView 对 URL 会有长度的限制,因此常规的通过 search 参数 进行传递的方式便具有一个问题,既 当需要传递的参数过长时,可能会导致被截断,例如传递 base64 或者传递大量数据时。
因此我们需要制定新的参数传递规则,我们使用的是函数调用的方式。
(二) Native 通知 Javascrip
由于 Native 可以算作 H5 的宿主,因此拥有更大的权限,上面也提到了 Native 可以通过 WebView API 直接执行 Js 代码。这样的权限也就让这个方向的通讯变得十分的便捷。
(三) JSBridge 的接入
JS 部分(bridge): 在 JS 环境中注入 bridge 的实现代码,包含了协议的拼装/发送/参数池/回调池等一些基础功能。
Native 部分(SDK):在客户端中 bridge 的功能映射代码,实现了 URL 拦截与解析/环境信息的注入/通用功能映射等功能。
将这两部分一起封装成一个 Native SDK,由客户端统一引入。客户端在初始化一个 WebView 打开页面时,如果页面地址在白名单中,会直接在 HTML 的头部注入对应的 bridge.js。这样的做法有以下的好处:
- 双方的代码统一维护,避免出现版本分裂的情况。有更新时,只要由客户端更新 SDK 即可,不会出现版本兼容的问题
- App 的接入十分方便,只需要按文档接入最新版本的 SDK,即可直接运行整套 Hybrid 方案,便于在多个 App 中快速的落地
- H5 端无需关注,这样有利于将 bridge 开放给第三方页面使用。
注意:协议的调用,一定是需要确保执行在 bridge.js 成功注入后。由于客户端的注入行为属于一个附加的异步行为,从 H5 方很难去捕捉准确的完成时机,因此这里需要通过客户端监听页面完成后,基于上面的回调机制通知 H5 端,页面中即可通过 **window.addEventListener(‘bridgeReady’,e=>{})**进行初始化。
(四) App 中 H5 的接入方式
将 H5 接入 App 中通常有两种方式:
(1) 在线 H5,这是最常见的一种方式。我们只需要将 H5 代码部署到服务器上,只要把对应的 URL 地址 给到客户端,用 WebView 打开该 URL,即可嵌入。该方式的好处在于:
- 独立性强,有非常独立的开发/调试/更新/上线能力
- 资源放在服务器上,完全不会影响客户端的包体积
- 接入成本很低,完全的热更新机制
但相对的,这种方式也有对应的缺点:
- 完全的网络依赖,在离线的情况下无法打开页面;
- 首屏加载速度依赖于网络,网络较慢时,首屏加载也较慢;
通常,这种方式更适用在一些比较轻量级的页面上,例如一些帮助页、提示页、使用攻略等页面。这些页面的特点是功能性不强,不太需要复杂的功能协议,且不需要离线使用。在一些第三方页面接入上,也会使用这种方式,例如我们的页面调用微信 JS-SDK。
(2) 内置包 H5,这是一种本地化的嵌入方式,我们需要将代码进行打包后下发到客户端,并由客户端直接解压到本地储存中。通常我们运用在一些比较大和比较重要的模块上。其优点是:
- 由于其本地化,首屏加载速度快,用户体验更为接近原生;
- 可以不依赖网络,离线运行;
但同时,它的劣势也十分明显:
- 开发流程/更新机制复杂化,需要客户端,甚至服务端的共同协作
- 会相应的增加 App 包体积
这两种接入方式均有自己的优缺点,应该根据不同场景进行选择
安卓
Cordova
Cordova 从入门到实践开发 - Fallen-down
Ionic
React Native
React、React native 从入门到实践开发 - Fallen-down
相关链接推荐:
H5 手机 App 开发入门:概念篇√
H5 手机 App 开发入门:技术篇√
Hybrid 应用中 H5 与 Native 通信的那点事儿
Hybrid App 技术解析 – 实战篇
什么是 Native、Web App、Hybrid、React Native 和 Weex?
JSBridge 深度剖析
例子
Hybrid App 从入门到实践开发
install_url
to use ShareThis. Please set it in _config.yml
.