您可以捐助,支持我们的公益事业。

1元 10元 50元





认证码:  验证码,看不清楚?请点击刷新验证码 必填



  求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Modeler   Code  
会员   
 
   
 
 
     
   
 订阅
  捐助
跨终端Web之Hybrid App
 
作者:徐凯 来源:InfoQQ 发布于: 2015-6-9
   次浏览      
 

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劣势表现为:

CPU/GPU密集类应用目前看更适合Native,例如极品飞车这样的游戏。这种劣势是在不断弱化的,正如 “CSS Transform 3D”引入GPU大大缓解了Web动画不流畅的问题。

静态资源从服务器端加载导致的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]

在受访的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]

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种。

重写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 { }

3. Bridge

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

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

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

Web端调用Bridge.callByJS({name:’func1’, callback: function(){}, param:{}}),由Bridge根据特定“Web调用Native”方式通知Native执行相应方法(图8-5中的“func1”)。

Native执行完毕后通过“Native调用Web”的方式调用Bridge. callByNative({token: ‘t1234’ })。如图8-6所示为Native调用Web时的Bridge时序图。

其中JavaScript回调函数会映射为字符串型的token,通过这个方式来保证最终触发JavaScript的回调函数(包括匿名函数和通过闭包实现的私有函数)。

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

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

Native端调用Bridge.callByNative({token:’t1234’, script: ‘//todo’}),由Bridge根据特定“Native调用Web”方式通知Web执行相应脚本。

Web执行完毕后通过“Web调用Native”的方式调用Bridge. callByJS({token: ‘t1234’ })。

如果Bridge.callByNative的script中执行了异步操作,需要在script主动调用Bridge.callByJS,并且不需要传token参数。

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

Bridge代码在产品环境下使用时请设置DEBUG = false。

避免在iOS下快速变化URL时造成的数据丢失,可以考虑使用队列机制缓存命令。

扩展至Windows Phone等平台时JavaScript部分只需要扩展invoke,Native代码可以参考Android的实现。

目前Bridge单次通信后会删除回调函数,如果需要多次调用缓存的回调函数(如连续监控传感器数据),可以扩展Bridge.callByNative。

代码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框架

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

Device API:封装Native的功能,跨平台提供一致的Device API。
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主要的功能为:

提供Hybrid API,可由JavaScript直接调用诸如加速度、摄像头、指南针、GPS、联系人等系统级API,完整的API列表请访问PhoneGap API Reference。
使用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设计的核心思路如下。

有一套核心的移动开发API,它们可以跨平台进行规范,这些方面的重点应放在代码重用上。

有针对特定平台的API、用户界面约定以及功能特性,开发者在针对该特定平台从事开发时采用,应该有针对特定平台的代码,以便这些用例提供最佳的用户体验。

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

2.2 工作流程

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

使用Titanium SDK在自带的IDE(ALLOY)中开发。

使用工具编译为平台相关的App。

图8-9 Titanium工作流

   
次浏览       
 
相关文章

手机软件测试用例设计实践
手机客户端UI测试分析
iPhone消息推送机制实现与探讨
Android手机开发(一)
 
相关文档

Android_UI官方设计教程
手机开发平台介绍
android拍照及上传功能
Android讲义智能手机开发
相关课程

Android高级移动应用程序
Android系统开发
Android应用开发
手机软件测试
最新活动计划
软件架构设计方法、案例与实践 8-23[特惠]
Linux内核编程及设备驱动 8-15[北京]
Python、数据分析与机器学习 8-23[特惠]
嵌入式软件架构设计 8-22[线上]
QT应用开发 9-5[北京]

android人机界面指南
Android手机开发(一)
Android手机开发(二)
Android手机开发(三)
Android手机开发(四)
iPhone消息推送机制实现探讨
手机软件测试用例设计实践
手机客户端UI测试分析
手机软件自动化测试研究报告
更多...   

Android高级移动应用程序
Android应用开发
Android系统开发
手机软件测试
嵌入式软件测试
Android软、硬、云整合

领先IT公司 android开发平台最佳实践
北京 Android开发技术进阶
某新能源领域企业 Android开发技术
某航天公司 Android、IOS应用软件开发
阿尔卡特 Linux内核驱动
艾默生 嵌入式软件架构设计
西门子 嵌入式架构设计
更多...