编者按:InfoQ开设新栏目“品味书香”,精选技术书籍的精彩章节,以及分享看完书留下的思考和收获,欢迎大家关注。本文节选自徐凯著《跨终端Web》第八章“Hybrid App”,主要讲述Hybrid App的发展现状以及技术实现,最后还介绍了两种主流Hybrid开发框架PhoneGap/Cordova和Titanium。
Native App(以下简称Native)和Mobile Web(以下简称Web)二者混合开发的产物被称为Hybrid App(以下简称Hybrid)。Hybrid并不是什么新概念,最早可以追溯到Symbian时代,直到iOS和Android出现之后才充分展现出价值。
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劣势表现为:
雏形
雏形阶段大致为:
发展
“跨平台”成了Hybrid最大的卖点,以PhoneGap[1]为首的Hybrid框架陆续出现,带来了诸多改变。
成熟
随着PhoneGap这类Hybrid框架在全球的流行,一些问题暴露了出来,也正是这些问题的解决,让Hybrid走向成熟。
以上便是Hybrid的发展概述,从国内最新的资料可以看出,Hybrid的趋势也是非常明显的。从图8-1可以看到越来越多的开发者决定使用Hybrid(跨平台技术),最近两年的总量已经有54%;而接近60%的开发者在Hybrid的技术方案上选择了PhoneGap。
图8-1 Hybrid在国内的发展情况[6]
在受访的2309个Mobile开发者中,到2013年8月为止完全使用Native开发的只有8%,而剩余的92%都可以被认为使用的是Hybrid,如图8-2所示。
图8-2 Hybrid使用情况
App的跨平台特性成为一个重要的考虑,如图8-3所示。
图8-3 跨平台特性受关注
图8-4显示了Hybrid惊人的增长速度:2013年无论是开发中、已发布的Hybrid(或HTML App)均相比于2012年出现了超过125%~400%的增长率[8]。
图8-4 Hybrid增长迅猛[9]
无论Android还是iOS,实现一个最简单的Hybrid App只需要几行代码:实例化WebView、加载页面,之后便是页面自身的代码。要想实现更为复杂的、完整的Hybrid还需要不少知识。
Mobile Web开发基础可以参考本书第2章,Native App开发基础已经超出本书的讨论范围,同样有很多可选择的书籍,本节来讲剩余的第3个问题 “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')" ];
“Native调用Web”本质上是JavaScript脚本的动态执行,在“Web调用Native”的场景下由于目前Native语言(Java和Objective-C)不容易像JavaScript那样便于动态执行,所以需要另辟蹊径。
2.1 Android
Android上常见的方式有3种。
重写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方法。
重写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。
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 { }
有了前两节的知识,可以实现一个通用模块(Bridge)来维护不同平台上的“Web与Native双向通信机制”功能。如图8-5所示为Web调用Native的Bridge时序图。
图8-5 Web调用Native的Bridge时序图
Web调用Native的实现原理如下。
图8-6 Native调用Web时的Bridge时序图
可以看到,Bridge实现“Native调用Web”是类似的。
笔者已经在Android上实现了完整的Bridge[10],Bridge由JavaScript实现可以运行在Android和iOS的WebView中,同时也非常容易扩展到Windows Phone等新平台,如代码8-7所示。
代码8-7 bridge.js
(function(window) { 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 invoke(cmd) { log('invoke', cmd); if (ANDROID) { prompt(cmd); } else if (IOS) { location.href = 'bridge://' + cmd; } else if (WP) { // TODO ... } } var Bridge = { callByJS: function(opt) { 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: function(opt) { 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框架通常提供以下功能。
PhoneGap几乎成了Hybrid的代名词,Titanium和PhoneGap的设计理念差异较大,图8-7形象地展示了PhoneGap和Titanium的组成部分。
图8-7 Hybrid框架[11]
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主要的功能为:
图8-8 PhoneGap编译打包功能
来自PhoneGap Showcase[14]和其他数据源的资料显示:
Titanium设计思路和PhoneGap有很大不同,Titanium目的为移动开发提供一种跨平台的JavaScript运行时环境和API。
Titanium设计的核心思路如下。
Titanium从设计理念上不追求“written once, run everywhere”,这是它的缺点,但同时它追求平台差异的更佳的用户体验,因而也受到一部分用户的追捧。Titanium的另一个缺陷是插件难于扩展,要想支持新平台则更加困难。
工作流程如下。Titanium工作流如图8-9所示。
图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做了第二次分享。