跨平台移动开发与Hybrid学习笔记-阿里云开发者社区

开发者社区> 开发与运维> 正文

跨平台移动开发与Hybrid学习笔记

简介:

最近做了iOS平台上Hybrid的调研,水平有限,结合以前尝试Hybrid的一点点经验做个小小的分析。概述了跨平台开发的的各种方案,并通过阅读Cordova-iOSWebViewJavascriptBridge的源码,分析Hybrid的实现原理和设计。

跨平台开发

这可能是移动开发领域的一个迷梦,无数人在用不同的方式想做好这一件事情,现在看起来还没有一套解决方案可以一统江湖。移动领域的跨平台开发可以从几个层面来切入(以下观点给予多年前的调研和最近的整理,出错了欢迎指出)。

交叉编译

交叉编译是用一种语言开发,编译成不同平台的机器码。可以复用的是商业逻辑以及数据存取部分。典型代表是Xamarin,包含收费版和功能有限的免费版。使用C#作为开发语言,完成共享代码部分的编写,通过封装好的 C# API 来访问和操控Native UI。支持iOS,Android,Mac及Windows。除了提供IDE外还提供了云端测试、监控平台,非常有吸引力。

编程语言翻译

将源编程语言翻译成不同平台的目标语言,实现一定程度上的代码复用。这类方案一般可以复用的是商业逻辑以及数据存取,适合逻辑复杂的应用。这样的解决方案有J2ObjC,免费开源。使用Java作为开发语言,在iOS平台翻译成OC。另外有Google Web Tookit可以将Java语言翻译为JavaScript,可以支持开发。实际应用可以阅读Google Inbox如何跨平台重用代码?,目前资源和成功示例还非常有限。Google放出了iOS示例项目J2ObjC sample问题多多,我还去提了个pull request。

容器兼容

同一种编程语言在多个平台运行,让容器成为语言和不同平台沟通的桥梁。这方面的先驱恐怕要数Java了吧,JVM完成了容器的作用。游戏平台普遍用这种这种方式,比如Cocos2d-xUnityCorona SDKCocos2d-x是用户最多的免费开源游戏开发平台,拥有发达的开发者社区。提供了C++和Lua API,支持iOS、Android、WinPhone 8, Windows, Mac OS X, Linux。Unity使用也非常广,提供了功能有限的免费版和收费版。Corona SDK占有率较小,使用Lua作为开发语言,以前收费现在免费。另外还有RubyMotion,用Ruby语言做跨平台开发。Ruby程序员的福音,但是价格略贵。

Semi-hybrid App

介于Native和Hybrid之间,也类似于容器兼容,容器是JavaScript运行环境。使用JavaScript开发的同时提供原生界面,这样可以在享受Hybrid开发便利的同时提供原生的用户体验。这方面有React NativeNative ScriptTitanium。React Native目前大热,支持iOS、Android和Web,目前只放出了iOS版。Native Script目前支持iOS、Android,Windows Phone在计划中。Titanium是一个老牌跨平台应用开发平台,支持Android、iOS 和Web。使用HTML、CSS、JavaScript开发,也支持PHP,Ruby和Python。当年在黑苹果上打开IDE点击运行,看到iPhone、Android和浏览器同时运行应用,内心还有点小激动呢。BeeFramework目前只支持iOS还不算跨平台,据说计划在1.0版本开始做跨平台。这里提到是因为他们最新的开发演示视频太酷了。

Hybrid App

Hybrid App顾名思义,就是混合了Web和Native的开发方式。它本质上应用是一个WebView,大多数开发基于HTML、CSS和JavaScript,Native只是一个壳。这方面的平台有PhoneGap/CordovaIntel XDK(原AppMobi XDK)和国内的产品AppCan 。有人会把Sencha Touch也列为Hybrid的一个平台。这样是不严谨的,实际上底层还是使用了Cordova来打包。

Mobile Web

纯浏览器解决方案,对Native的能力使用十分有限。优点嘛,就是更新方便,无需安装。

Hybrid App

这种开发模式之所以出现是由于它有一些优点:

  • 一次开发,多处运行
  • 多平台一致的用户体验
  • 学习和开发成本低
  • 更新方便
  • 适合内容展示类应用

当然,这种开发方式也有一些弊端:

  • 用户体验不如针对平台的应用
  • 性能和Native有差距
  • 安全性差
  • 不适合功能复杂的应用

我个人的理解是,Hybrid合理的应用场景是应用功能并不复杂,不苛求用户体验,人员和时间有限。 之前看到的一个例子是ROR作者谈Hybrid开发,他们在开发37signal的Basecamp应用时通过Hybrid开发方式在已有的移动网站的基础上用1名程序员和1名设计师开发出了iOS 版本。目前应用由90% HTML +10%原生组成,会选择最值得做原生开发的那10%的部分来做原生开发。

我花了些时间挑选了两个比较有代表性的Hybrid项目,研究了它们的结构和实现细节。这两个项目分别是WebViewJavascriptBridge和Cordova-iOS。

WebViewJavascriptBridge

WebViewJavascriptBridge是一个不错的JavaScript与Native之间双向通信的库,多个厂家包括Facebook在使用。项目结构简单,非常适合掌握Hybrid的实现原理。Native与JS间通信使用Web View来作为媒介。它们与Web View之间的交互放到了bridge层来处理,分别是Native Bridge和JS Bridge。首先需要学习的是Web View加载完成后Native给JS发消息的时序。可以分为3个阶段:

  • Native发消息到Native Bridge

Native把要调用的JS的handler name和callback传给Native Bridge。Native Bridge会生成callback id,保存callback。

  • Native Bridge发出消息,JS执行完毕通知Native Bridge

Native Bridge将消息(包含callback id和handler name)发到JS Bridge,JS Bridge根据handler name找到合适的handler并执行,执行完毕后将消息(包含callback id和结果)存到消息队列中,并通知Native Bridge。handler执行过程可能是异步的。

  • Native Bridge从JS取回消息执行callback

Native Bridge从JS的消息队列中取回消息,并根据其中的callback id找到之前保存的callback,执行。

Sequence1

上图中Native Bridge与JS Bridge间的消息发送是简化的,实际上都需要经过Web View。Native Bridge给JS Bridge发消息的关键方法是UIWebView的stringByEvaluatingJavaScriptFromString:


- (NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;


JS Bridge给Native发消息时,将消息先保存,然后通过iframe加载一个特殊的url。Native Bridge拦截到这个url后,通过调用上面的stringByEvaluatingJavaScriptFromString方法来拿到消息。

如果是在Web View加载完成前发送消息,Native Bridge并不会立即发消息,而是将消息存到启动队列中。Web View在加载过程中,JS可以注册各种handler。加载完成后,Native Bridge将启动消息队列中的消息发出,之后的执行过程不变。时序图如下:

Sequence2

JS给Native发消息的过程非常类似:

Sequence3

这个库有一些值得注意的特性或者实现细节:

  • Web View加载完成前,Native对JS的调用存储直到完成后发出
  • webViewDidStartLoad可能执行多次,需要计数并在全部加载完成时处理启动队列
  • 项目已经有兼容WKWebView和UIWebView的pull request,并未合并到主干

Cordova-iOS

这里有必要说一下Cordova和PhoneGap的关系。简单说来就是Cordova是PhoneGap的开源部分,PhoneGap是Cordova的发行版。有兴趣深入了解可以阅读PhoneGap, Cordova, and what’s in a name?。三年多前接过一个外包,要出Android和iOS两个版本,时间紧但用户体验要求不高。当时做选型,看过当时有的几个框架PhoneGap、Titanium、Corona SDK等,选择了学习成本最低开发最快的PhoneGap。最后也在很短时间内完成了开发。学习源码时我发现Cordova-iOS的源码中一些插件(比如定位、电池状态、联系人等)的实现放在了各自的库中。下载PhoneGap后打开其中的iOS 工程会看到加入各种插件后的源代码,学习起来更方便。Cordova有一些好的特色,值得学习:

配置文件

应用的管理功能采用了配置文件(Android下为xml文件,iOS下为plist文件)。在配置文件中可以管理所有应用内使用的插件、日志级别、访问的ip白名单、开启历史记录等

插件管理

Cordova将各种平台的一些原生功能封装成插件提供。新建应用的时候可以通过应用的配置文件指定使用哪些插件。一个典型的插件,比如设备信息可以参见Cordova plugin device

建立一个插件的方法可以参考PhoneGap 插件指南。这里简单介绍一下建立插件的方法。首先需要先定义一个插件配置文件plugin.xml,在其中指定各个平台的实现的源代码和使用这个功能的JS文件。在iOS平台,需要新建一个类继承CDVPlugin,定义一些方法供外部调用。在方法内调用其commandDelegatesendPluginResult方法即可完成返回结果给JS。例如:


- (void)getDeviceInfo:(CDVInvokedUrlCommand*)command
{
    NSDictionary* deviceProperties = [self deviceProperties];
    CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:deviceProperties];

    [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}


在JS文件中定义这个插件的调用接口,即可在项目中使用:


Device.prototype.getInfo = function(successCallback, errorCallback) {
    argscheck.checkArgs('fF', 'Device.getInfo', arguments);
    exec(successCallback, errorCallback, "Device", "getDeviceInfo", []);
};


插件加载

读取配置文件后并不立即初始化插件。在首次使用时初始化并保存到字典中。遇到内存警告时回收插件。

白名单

白名单功能控制允许加载的schema、域名或者IP、URL path,支持wildcard通配。web view发出的请求如果的scheme是http/https/ftp/ftps,并且符合白名单规则的用web view打开。如果scheme是gap,则触发Native从JS环境中获取任务队列。这一点与WebViewJavascriptBridge很类似。

多种JS调用Native模式

Cordova支持多种JS向Native发送信息的模式:

  • IFRAME_NAV
  • XHRNOPAYLOAD
  • XHRWITHPAYLOAD
  • XHROPTIONALPAYLOAD
  • IFRAMEHASHNO_PAYLOAD
  • IFRAMEHASHWITH_PAYLOAD
  • WKWEBVIEWBINDING

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

分享:
开发与运维
使用钉钉扫一扫加入圈子
+ 订阅

集结各类场景实战经验,助你开发运维畅行无忧

其他文章