把握行业变革关键节点,12 月 19 日 - 20 日,AICon北京站即将重磅启幕! 了解详情
写点什么

跨终端 Web 之 Hybrid App

  • 2015-05-26
  • 本文字数:7368 字

    阅读完需:约 24 分钟

编者按:InfoQ 开设新栏目“品味书香”,精选技术书籍的精彩章节,以及分享看完书留下的思考和收获,欢迎大家关注。本文节选自徐凯著《跨终端Web》第八章“Hybrid App”,主要讲述Hybrid App 的发展现状以及技术实现,最后还介绍了两种主流Hybrid 开发框架PhoneGap/Cordova 和Titanium。


Native App(以下简称 Native)和 Mobile Web(以下简称 Web)二者混合开发的产物被称为 Hybrid App(以下简称 Hybrid)。Hybrid 并不是什么新概念,最早可以追溯到 Symbian 时代,直到 iOS 和 Android 出现之后才充分展现出价值。

Hybrid 简史

1. 背景

Hybrid 既利用了 Native App 丰富的设备 API(Device API),又能拥有 Mobile Web 的跨平台、高效开发、快速发布的能力,对于相当庞大的应用场景而言都是适用的。

Hybrid 优势在于:

  • 跨平台 Web 内容可以做到开发一次,所有平台生效,诸多产品需要这种能力。

  • 快速发布 iOS 平台,Apple Store 平均审核周期 1~2 周不等,甚至更长,产品的发布周期从 2 周到 1 月,这对需要快速发布的产品而言难以接受。

    Android 平台,应用商店众多,发布过程烦琐。虽然可以应用内升级,但是带来的问题是新 App 需要通过应用商店,此外 APK 体积庞大,2G/3G 环境下体验差。

  • 高效开发 Web 开发经过 20 年的发展,已经将结构(HTML)、表现(CSS)、行为(JavaScript)3 部分很好地分离开,在分工协作、开发效率上会具明显优势。

  • 丰富的 Device API Web(HTML5)强调通用性,受限于标准和浏览器实现,许多有用的系统功能未能得到支持(或部分支持)。而 Native 最大的优势在于设备 API 的调用能力,只要桥接 Native 和 Web,Web 也就能够拥有这种能力。

Hybrid 劣势表现为:

  1. CPU/GPU 密集类应用目前看更适合 Native,例如极品飞车这样的游戏。这种劣势是在不断弱化的,正如 “CSS Transform 3D”引入 GPU 大大缓解了 Web 动画不流畅的问题。
  2. 静态资源从服务器端加载导致的 UI 展示延迟问题。这个问题可以通过 Native 拦截 WebView 通信加载已打包的公共库来缓解。

2. 简史

  • 雏形 雏形阶段大致为:

    • Symbian V3/5 时代已经有 Hybrid 雏形。
    • iOS 最初的 App 都是由 Objective-C 编写而成的,受限应用商店的发布周期,内容经常变化的部分开始通过使用内置浏览器控件(WebView)加载服务端页面来实现。
    • Android 出现并流行之后,可以将更多的 App 功能通过 Hybrid 来实现,这样在不同平台上就可以只维护一个版本。
  • 发展 “跨平台”成了 Hybrid 最大的卖点,以 PhoneGap[1] 为首的 Hybrid 框架陆续出现,带来了诸多改变。

    • 访问设备功能。
      • Web(HTML5)不支持的功能可以让 Native 实现,再通过 Native 和 Web 之间通信,通过这种方式可以让 Web 获得和 Native 相同的设备 API 调用能力,这是 PhoneGap 这类 Hybrid 框架的基本工作原理。
      • 与此同时,将 Web 代码转为 Native 的 Hybrid 框架(如 Tianium[2])也出现了。
    • PhoneGap 子项目 weinre 是一种远程调试工具,极大地缓解了 Hybrid 难于调试的问题,进一步促进了 Hybrid 的发展。
    • Hybrid 框架提供了应用打包功能,开发者可以完全使用 HTML、CSS、JavaScript 开发 Native App。
  • 成熟 随着 PhoneGap 这类 Hybrid 框架在全球的流行,一些问题暴露了出来,也正是这些问题的解决,让 Hybrid 走向成熟。

    • 开发体验提升。
      • weinre 这类调试工具仍属于插件性质,诸如“网络”、“本地资源”等高级调试功能无法支持,WebView 的原生调试需求越来越强烈。
      • iOS 6.0+ 已经支持原生的远程调试 [3]。
      • Chrome for Android 在原生远程调试上处于领先地位 [4]。
      • 从 Android 4.4 开始,WebView 也支持原生的远程调试 [5]。
    • 提升 WebView 性能的呼声日益增强。
    • 某些追求极致性能的功能转由 Native 实现,如转场(页面间切换)动画。
    • 静态资源本地化是理想状态,其他场景下 Native 拦截 WebView 的请求,并让公共资源重定向到 App 内置资源,同样能实现为 Web 提速。

3. 现状

以上便是 Hybrid 的发展概述,从国内最新的资料可以看出,Hybrid 的趋势也是非常明显的。从图 8-1 可以看到越来越多的开发者决定使用 Hybrid(跨平台技术),最近两年的总量已经有 54%;而接近 60% 的开发者在 Hybrid 的技术方案上选择了 PhoneGap。

图 8-1 Hybrid 在国内的发展情况 [6]

  1. 在受访的 2309 个 Mobile 开发者中,到 2013 年 8 月为止完全使用 Native 开发的只有 8%,而剩余的 92% 都可以被认为使用的是 Hybrid,如图 8-2 所示。

图 8-2 Hybrid 使用情况
2. App 的跨平台特性成为一个重要的考虑,如图 8-3 所示。

图 8-3 跨平台特性受关注

图 8-4 显示了 Hybrid 惊人的增长速度:2013 年无论是开发中、已发布的 Hybrid(或 HTML App)均相比于 2012 年出现了超过 125%~400% 的增长率 [8]。

图 8-4 Hybrid 增长迅猛 [9]

Hybrid 技术

无论 Android 还是 iOS,实现一个最简单的 Hybrid App 只需要几行代码:实例化 WebView、加载页面,之后便是页面自身的代码。要想实现更为复杂的、完整的 Hybrid 还需要不少知识。

  1. Mobile Web 开发基础:HTML、CSS、JavaScript。
  2. Native App 开发基础:Android、iOS。
  3. Native 与 Web 双向通信机制。

Mobile Web 开发基础可以参考本书第 2 章,Native App 开发基础已经超出本书的讨论范围,同样有很多可选择的书籍,本节来讲剩余的第 3 个问题 “Native 与 Web 双向通信机制”。

1. Native 调用 Web

无论 Android 还是 iOS ,Native 调用 Web(JavaScript) 都有很好的原生支持,如代码 8-1 和代码 8-2 所示。Android 中的调用方式如下,其中 webView 是 Webview 的实例。

代码 8-1 Android 调用 JavaScript

复制代码
webView.loadUrl("javascript:(function(){ alert(‘ok’); })()”);

iOS 中的调用方式如下,其中 webView 是 UIWebview 的实例。

代码 8-2 iOS 调用 JavaScript

复制代码
[webView stringByEvaluatingJavaScriptFromString: @"alert('ok')" ];

2. Web 调用 Native

“Native 调用 Web”本质上是 JavaScript 脚本的动态执行,在“Web 调用 Native”的场景下由于目前 Native 语言(Java 和 Objective-C)不容易像 JavaScript 那样便于动态执行,所以需要另辟蹊径。

2.1 Android

Android 上常见的方式有 3 种。

  1. 重写 WebViewClient.shouldOverrideUrlLoading(如代码 8-3 所示)。 代码 8-3 重写 WebViewClient.shouldOverrideUrlLoading
复制代码
webView.setWebViewClient(new WebViewClient(){
@Override
public boolean shouldOverrideUrlLoading (WebView view, String url){
// TODO 解析 URL 并触发 Native 代码
return true;
}
});

当页面内的 URL 发生变化时,如点击链接、执行 JavaScript(如 _location.href=http://)_ 等均会触发 WebViewClient.shouldOverrideUrlLoading,通过将 Web 调用 Native 的数据封装在 URL,再由 Native 解析数据并执行响应 Native 方法。
2. 重写 WebChromeClient.onJsPrompt,或 onJsConfirm,或 onJsAlert,以 WebChromeClient.onJsPrompt 为例,如代码 8-4 所示。 代码 8-4 重写 WebChromeClient.onJsPrompt

复制代码
webView.setWebChromeClient(new WebChromeClient() {
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
// TODO 解析 message 并触发 Native 代码
result.confirm("");
return true;
}
});

当执行“window.prompt(“{}”)”这样的 JavaScript 代码时,将会触发 WebChromeClient.onJsPrompt。
3. WebView.addJavascriptInterface,这种方式和前两种都不同,通过将 Java Object(A) 映射为 JavaScript Object(B),从而调用 B.func1 时将会自动触发 A.func1,通过这种原生的方式实现了 “Web 调用 Native”,如代码 8-5 所示。 代码 8-5 WebView.addJavascriptInterface

复制代码
webView.addJavascriptInterface(new Object() {
public void func1() {
}
public void func2() {
}
}, "webViewObj");

以上 3 种方式,最常用的是方式 2;方式 2 相比方式 1 有内置的队列支持,不会出现高频访问数据丢失的情况;方式 3 是 Android 原生方式,但是不如前两种方式灵活。

2.2 iOS

iOS 中可用的方式类似 Android 中的 WebViewClient.shouldOverrideUrlLoading, 通过监控 WebView 的 URL 变化实现 Web 调用 Native,如代码 8-6 所示。

代码 8-6 shouldStartLoadWithRequest

复制代码
- (BOOL)webView:(UIWebView *) webView shouldStartLoadWithRequest:
NSURLRequest *)request
navigationType: (UIWebViewNavigationType)navigationType {
}

3. Bridge

有了前两节的知识,可以实现一个通用模块(Bridge)来维护不同平台上的“Web 与 Native 双向通信机制”功能。如图 8-5 所示为 Web 调用 Native 的 Bridge 时序图。

图 8-5 Web 调用 Native 的 Bridge 时序图

Web 调用 Native 的实现原理如下。

  1. Web 端调用 Bridge.callByJS({name:’func1’, callback: function(){}, param:{}}),由 Bridge 根据特定“Web 调用 Native”方式通知 Native 执行相应方法(图 8-5 中的“func1”)。
  2. Native 执行完毕后通过“Native 调用 Web”的方式调用 Bridge. callByNative({token: ‘t1234’ })。如图 8-6 所示为 Native 调用 Web 时的 Bridge 时序图。
  3. 其中 JavaScript 回调函数会映射为字符串型的 token,通过这个方式来保证最终触发 JavaScript 的回调函数(包括匿名函数和通过闭包实现的私有函数)。

图 8-6 Native 调用 Web 时的 Bridge 时序图

可以看到,Bridge 实现“Native 调用 Web”是类似的。

  1. Native 端调用 Bridge.callByNative({token:’t1234’, script: ‘//todo’}),由 Bridge 根据特定“Native 调用 Web”方式通知 Web 执行相应脚本。
  2. Web 执行完毕后通过“Web 调用 Native”的方式调用 Bridge. callByJS({token: ‘t1234’ })。
  3. 如果 Bridge.callByNative 的 script 中执行了异步操作,需要在 script 主动调用 Bridge.callByJS,并且不需要传 token 参数。

笔者已经在 Android 上实现了完整的 Bridge[10],Bridge 由 JavaScript 实现可以运行在 Android 和 iOS 的 WebView 中,同时也非常容易扩展到 Windows Phone 等新平台,如代码 8-7 所示。

  1. Bridge 代码在产品环境下使用时请设置 DEBUG = false。
  2. 避免在 iOS 下快速变化 URL 时造成的数据丢失,可以考虑使用队列机制缓存命令。
  3. 扩展至 Windows Phone 等平台时 JavaScript 部分只需要扩展 invoke,Native 代码可以参考 Android 的实现。
  4. 目前 Bridge 单次通信后会删除回调函数,如果需要多次调用缓存的回调函数(如连续监控传感器数据),可以扩展 Bridge.callByNative。

代码 8-7 bridge.js

复制代码
functionwindow{
var DEBUG = true;
var callbacks = {};
var guid = 0;
var ua = navigator.userAgent;
// TODO 精确性待改进
var ANDROID = /android/i.test(ua);
var IOS = /iphone|ipad/i.test(ua);
var WP = /windows phone/i.test(ua);
//ANDROID = 0; IOS = 1;
/**
* 方便在各个平台中看到完整的 log
*/
function log() {
if (DEBUG) {
console.log.call(console, Array.prototype.join.call(arguments, ' '));
}
}
/**
* 平台相关的 Web 与 Native 单向通信方法
*/
function invokecmd{
log('invoke', cmd);
if (ANDROID) {
prompt(cmd);
}
else if (IOS) {
location.href = 'bridge://' + cmd;
}
else if (WP) {
// TODO ...
}
}
var Bridge = {
callByJS: functionopt{
log('callByJS', JSON.stringify(opt));
var input = {};
input.name = opt.name;
input.token = ++guid;
input.param = opt.param || {};
callbacks[input.token] = opt.callback;
invoke(JSON.stringify(input));
},
callByNative: functionopt{
log('callByNative', JSON.stringify(opt));
var callback = callbacks[opt.token];
var ret = opt.ret || {};
var script = opt.script || '';
// Native 主动调用 Web
if (script) {
log('callByNative script', script);
try {
invoke(JSON.stringify({
token: opt.token,
ret: eval(script)
}));
} catch (e) {
console.error(e);
}
}
// Web 主动调用 Native,Native 被动响应
else if (callback) {
callback(ret);
try {
delete callback;
log(callbacks);
} catch (e) {
console.error(e);
}
}
}
};
window.Bridge = Bridge;
window.__log = log;
})(window);

Hybrid 框架

目前一个 Hybrid 框架通常提供以下功能。

  1. Device API:封装 Native 的功能,跨平台提供一致的 Device API。
  2. App 打包:将 HTML5 编写的代码打包为 App(Titanium 会转换代码)。

PhoneGap 几乎成了 Hybrid 的代名词,Titanium 和 PhoneGap 的设计理念差异较大,图 8-7 形象地展示了 PhoneGap 和 Titanium 的组成部分。

图 8-7 Hybrid 框架 [11]

1. PhoneGap

1.1 PhoneGap 和 Cordova

PhoneGap 开发商 Notibi 2010 年将 PhoneGap 代码贡献给 Apache 软件基金(ASF),PhoneGap 核心引擎成为新的开源项目 Cordova,同时 PhoneGap 成了 Cordova 的一个发行版本 [12]。2011 年 10 月,Notibi 被 Adobe 收购 [13],但没有影响到 PhoneGap 和 Cordova 的开源性质。

1.2 原理

written once,run everywhere

如引文所述“一处编写,多处运行”,PhoneGap 主要的功能为:

  1. 提供 Hybrid API,可由 JavaScript 直接调用诸如加速度、摄像头、指南针、GPS、联系人等系统级 API,完整的 API 列表请访问 PhoneGap API Reference。
  2. 使用 Web(HTML、CSS、JavaScript)开发的内容经过 PhoneGap 编译打包为各个平台的 Native App,如图 8-8 所示。

图 8-8 PhoneGap 编译打包功能

1.3 经典案例

来自 PhoneGap Showcase[14] 和其他数据源的资料显示:

  • Facebook Mobile SDK[15] 和 SalesForce Mobile SDK[16] 均是基于 Cordova 的分支开发的。
  • Facebook 客户端中 Web 代码超过 90%[17]。
  • LinkedIn iPad 客户端中 Web 代码甚至超过 95%。
  • Wikipedia 更是直接用 PhoneGap 开发了自己的 iOS/Android Hybrid App[18],并将代码在 GitHub 上开源 [19]。

2. Titanium

Titanium 设计思路和 PhoneGap 有很大不同,Titanium 目的为移动开发提供一种跨平台的 JavaScript 运行时环境和 API。

2.1 设计思路

Titanium 设计的核心思路如下。

  1. 有一套核心的移动开发 API,它们可以跨平台进行规范,这些方面的重点应放在代码重用上。
  2. 有针对特定平台的 API、用户界面约定以及功能特性,开发者在针对该特定平台从事开发时采用,应该有针对特定平台的代码,以便这些用例提供最佳的用户体验。

Titanium 从设计理念上不追求“written once, run everywhere”,这是它的缺点,但同时它追求平台差异的更佳的用户体验,因而也受到一部分用户的追捧。Titanium 的另一个缺陷是插件难于扩展,要想支持新平台则更加困难。

2.2 工作流程

工作流程如下。Titanium 工作流如图 8-9 所示。

  1. 使用 Titanium SDK 在自带的 IDE(ALLOY)中开发。
  2. 使用工具编译为平台相关的 App。

图 8-9 Titanium 工作流

书籍简介

移动互联网不可阻挡地进入了我们的生活。作者将自己在百度和天猫期间的跨终端 Web 的开发实践转化为书中的技术方案和实现,呈现给各位读者。第 1 章提出了跨终端 Web 的概念以及实现跨终端 Web 的多重途径,第 2 章主要介绍 Mobile Web 的技术基础,第 3~7 章是全书的核心,按照开发流程组织逐步讲解了实现跨终端 Web 所需要的各类技术基础设施,第 8 章主要介绍了 Hybrid App 的发展历程、实现细节以及成熟的框架,第 9 章介绍的跨终端存储方案(Storage)是作者曾经的冠军作品,第 10 章完整介绍了如何通过脚本录制和回放来实现跨终端动作同步。

《跨终端 Web》讲解深入浅出,通畅易懂,适合有一定 PC Web 基础,希望迅速了解 Mobile Web,致力于 PC 和 Mobile Web 技术融合的读者。

作者简介

鬼道(原名徐凯),2011 年毕业于同济大学计算机系,模式识别方向硕士研究生。曾就职百度,现为天猫前端通用组技术 Leader。本书源于 2013 年 7 月在 D2 上的主题分享“移动优先的跨终端 Web”,2013 年 11 月在 W3CTECH 2013 做了第二次分享。


2015-05-26 21:509549

评论

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

华为云DTSE携手“灵康宜”构造一站式智慧健康检测云平台

华为云开发者联盟

云计算 后端 华为云 华为云开发者联盟 华为云DTSE

软件测试学习笔记丨Linux命令 wc统计

测试人

软件测试

Flink 2.0 状态管理存算分离架构演进

Apache Flink

喜报|「云原生数据库PolarDB」、「阿里云瑶池一站式数据管理平台」揽获“2023技术卓越奖”

阿里云瑶池数据库

数据库 云计算 阿里云 云原生

计算机领域的 out of the box 特性是什么?

伤感汤姆布利柏

比特币 ETF 费用战蔓延至欧洲

TechubNews

论如何在多模态大模型下实现“找片儿”的艺术

不在线第一只蜗牛

人工智能 视频 大模型

软件测试学习笔记丨Selenium常见控件定位方法(八大定位方式)

测试人

软件测试

纯血鸿蒙来了,企业开发者应该关注什么

不在线第一只蜗牛

华为 架构 企业开发 鸿蒙系统

软件测试|从零到一:我的测试开发工程师之路

霍格沃兹测试开发学社

知识图谱之图数据库如何选型:知识图谱存储与图数据库总结、主流图数据库对比(JanusGraph、HugeGraph、Neo4j、Dgraph、NebulaGraph、Tugrapg)

汀丶人工智能

图数据库 知识图谱

区块链数据分析:揭示加密经济投资的真相与机会

Footprint Analytics

区块链 加密货币

精选案例|首创证券 NoETL 敏捷数据分析创新实践

Aloudata

数仓 ETL

Easysearch:语义搜索、知识图和向量数据库概述

极限实验室

向量数据库 语义搜索 easysearch 知识图 知识概述

DDD技术方案落地实践

京东零售技术

后端 DDD 系统架构 开发 提效

测试管理| 从零到一:我的测试开发工程师之路

测吧(北京)科技有限公司

测试

搜索推荐DeepFM算法详解:算法原理、代码实现、比赛实战

汀丶人工智能

自然语言处理 排序算法 搜索推荐 召回算法 DeepFM

让错误码规范起来吧

京东科技开发者

人太多,挤不进去?教你搭建一个自己的幻兽帕鲁服务器

华为云开发者联盟

云计算 后端 华为云 华为云开发者联盟 服务器搭建

为什么美国CN2服务器是海外业务的首选?租用攻略详解

一只扑棱蛾子

美国服务器 CN2服务器

微前端框架MicroApp 1.0正式发布

京东零售技术

开源 前端 微前端 提效

DAPP二二复制矩阵公排合约系统开发丨源码详情

l8l259l3365

分钟级实时数据分析的背后——实时湖仓产品解决方案

袋鼠云数栈

大数据 湖仓一体 实时湖仓

湖仓新范式的造浪者 | StarRocks 2023 年度总结(文末福利)

StarRocks

数据库 StarRocks #数据分析

Python文件写入不稳定的处理方法

麦兜

京东广告算法架构体系建设--在线模型系统分布式异构计算演变 | 京东零售广告技术团队

京东科技开发者

跨终端Web之Hybrid App_移动_徐凯_InfoQ精选文章