如何开发一个移动跨平台库

移动开发
本文中,我介绍了我在探索开发移动跨平台库(例如一个codebase,可成为不同移动平台上的app的一部分)时积累的经验:从移动跨平台开发工具(PhoneGap,Titanium之类的),到代码移植工具;从无法满足需求的 WebView,到可以满足需求的 C++ 和 JavaScript 引擎。在这个问题上,现有的教程资源比较匮乏,因此我们想分享一些经验可能会有帮助。

“一次编写,到处运行” 是为阐明 Java 的跨平台功能而编写的著名标语。程序员都希望他们的代码能到处运行。尽管如此,要跟上 CPU 体系结构的变化是如此困难,新的程序语言日日涌现,框架来了又去,并且如果你想跟操作系统打交道,代码的重用性就不必再提。

但是也许,如果我们只把范围限制在移动设备,可能还有机会!近年来移动化的趋势日趋明显,所以开发一个移动跨平台的一定会对一些开发者有所帮助。

如果我们要进一步缩小,到iOS和Android上,它们目前的市场占有率为93.9%。 这给我们锁定了7个目标CPU架构/ABI(ARMv7、armv7s、IOS的arm64、armeabi、armeabi-V7A、x86和针对 Android的MIPS),以及两种编程语言(iOS的Objective-C和Android的Java)。至于框架和操作系统,支持最新的两个 iOS版本应该就足够了,因为新的iOS版本有着较高的使用率,但当涉及到Android时,要有一个不错覆盖率的话我们需要支持Froyo(或者Gingerbread)以后的所有版本。正如你所看到的,这不是件容易的事,但我们需要这样做。 

我们想要做的总结在下图中;在有一些适配特定平台胶水代码的情况下在两个平台间共享一个库。由于Skyscanner严重依赖在于互联网,一些网络函数是不可少的。

The high-level architecture of the mobile apps

通常情况下,在iOS中,一个库可以通过Objective-C源代码或预编译的静态二进制库 中导入,并要有相应的头文件。在Android中,除了Java源代码,一个库也可以通过.class(字节码)文件和静态/共享二进制库导入。然而,由 于这些选择是限制性的,这里的研究将更进一步,去探索在Android和iOS中导入代码的替代方法。 

那么,我们该如何开始呢?有什么选项呢?是否有简化的工具呢?

选项1-移动跨平台开发工具

如果你是一名移动开发者,你一定听说过大量的移动跨平台开发工具,比如PhoneGap,Appcelerator Titanium 和Xamarin。它们中的一些工具允许我们开发类库,对吗?

具备这一功能的工具通常存在的主要问题如下:

 1输出到终端产品(.app/ .ipa或者 .apk)而不是类库中

 2嵌入到运行时的环境中运行跨平台的代码,这些代码与在环境中的本地代码交互非常的困难.

  • 网页视图工具-这些工具使用视图做为运行时环境,用JavaScript/HTML5来编写代码.如果它们与运行在网页视图的代码交互困难时,它们将会立即失效.(然而,它总是试图这样,你随后将会发现.)这样的工具包括PhoneGap, RhoMobile,Secha Touch,appMobi, Telerik.

  • Adobe AIR--这的运行时环境中Adobe集成运行时环境.它的失效取决与与AIR中代码交互的困难程度.

  • Xamarin--它只能输出到中间的Xamarin 库中,而不是本地库中.

  • Appcelerator Titanium--创建的本地类库并不是官方支持的,但是它可能是可以工作的,如果我们写Titanium的扩展,这些扩展允许与本地代码交互.太多的麻烦,存在问题的那些结果,同时不确保在下一个版本Titanium的升级中这些功能是否保留.

  • Corona--Corona的员工声称它已支持Android,同时它未来将支持iOS.

  • MoSync--与Corona类似

  • Kony--不支持.

  • Trigger.io--不支持

  • OpenFL--不支持

  • DragonRad--已过时,似乎不支持

因此,失败了,没什么真正可行的:(

但是等一下,难道C/C++的代码不能访问iOS和Android吗?

选项2--C++

使用C++来开发类库是可行的两个解决方案之一。

在Android平台,本地开发套件(NDK)和Java本地套件框架(JNI)允许Java与C/C++的代码运行和交互.NDK的负责为 Android的每个目标对象(armeabi,armeabi-v7a,x86和mips)编译C++代码; 而JNI允许这两种语言沟通交流.使用JNI相当的啰唆;程序员必须遵守命名规则,而且需要用Java和c++两层包装.一方面,通过用Java语言暴露 所有的c++类和方法(包括了本地关键字),Java封装提供了一个用于c++类库的Java的接口.另一方面,c++封装提供了Java封状与c++类 库之间的桥梁,这两种语言的对象可以相互转化。

在iOS中,事情就变得简单多了。在此系统中,没有命名规则,只需要采用 “Objective-C++”进行额外一层的封装就可以。“Objective-C++”是一种允许变量在单一的源文件中既使用“Objective- C”代码,也可以使用“C++”代码的语言。所以,所有的对象翻译都只发生在这个单一封装层中。你可以查看略微修改后的Android/iOS应用的流程 图如下:

引入第三方库也是不常规的,因为程序员不能直接访问JRE/Android以及Cocoa Touch框架。在这种情况下,第三方库可以通过两种方式引入,源代码或预编译的二进制文件(或者找到它们,或者编译它们)。其中的一个特例是执行网络操 作(HTTP请求),在标准模板库(STL)中它是不被支持的,所以我们整合了libcurl到跨平台库中。libcurl不能以源代码引入,只能作为一 个可执行的配置脚本。幸运的是能够找到为iOS预编译的二进制文件。在Android中,我们使用NDK工具链/编译器为每个Android目标系统编译 libcurl。为7个目标架构(3适用于iOS,4为Android)编译库是很费时的,但这个过程的一部分可以用脚本实现自动化。

这个措施相当奏效,C++是种流行的语言,它有一个庞大数量的可用的第三方库,并且所有使用的 工具(Android的NDK、JNI、Objective-C++)都有官方的解决方案,由谷歌和苹果的支持。这个措施的唯一的缺点是在Android 上,如果我们想保持Java包装对象对C++对象的引用,我们必须在Java对象释放前手动回收C++对象(通常叫删除C++对象)。然而,如果没有理由 保留C++对象的话,它们可以在被复制成Java中对应部分后立即销毁。

选项 3 - 代码移植

另一个考虑过的选择是,只维护一个代码库,然后用适当的工具把代码翻译为平台对应的语言。这种选择也有它的缺陷:

  • 生成代码效率不会像原生开发者写的那样高。

  • 翻译过程很容易引入Bug,而且必须手动修复。

  • 导入的二进制文件很难被翻译,因为大多数的工具只能翻译源代码。

以下是几种移动平台代码移植工具。遗憾的是,没有一种能满足需求:

  • J2ObjC - Google 开发的工具,用于翻译 Java 代码为 Objective-C 。看起来质量比较高(与以下其他相比)。目前为止,它能把部分 Java 类翻译为 Objective-C ,但开发还没有完成。不幸的是,它目前翻译不了 Java 的 HTTP 请求,但是如果我们为每个平台单独实现这部分功能,也有实现的可能。这个项目从2012年9月建立至今。

  • Hyperloop - 将 JavaScript 翻译为平台原生代码的工具。目前为止,它只支持 iOS ,而且并不稳定,但他们的计划是扩展到所有流行的平台。这个项目从2013年8月建立至今。

  • ObjC2J - 将 Objective-C 翻译为 Java 的工具。这本来也是个很好的思路,但不幸的是,它还不够成熟,含有很多bug,经常输出不能编译的代码。

  • XMLVM - 将 JVM 字节码交叉编译为 Objective-C 的工具。这个工具不仅不够完善,用起来很复杂,并且需要下载/导入很多legacy jar。

  • Apportable - 将 iOS 应用转化为 Android 应用的工具。不幸的是,它达不到我们的要求,因为它只能翻译整个应用,无法翻译库,而且直接输出 .apk(Android 应用安装包)文件。

  • Avian - 轻量级的 Java 虚拟机,可以嵌入 iOS app bundle 并运行 Java 代码。这个方案满足不了需求,因为想要让 iOS 上跑的 UI 代码与虚拟机中跑的 Java 库代码交互非常困难。

  • in the box - 在 iOS 上运行的移植 Dalvik 虚拟机和 Android Gingerbread (2.3) API。这个选项被否决了,因为这个项目已经失效。

选项4 - WebView中的JavaScript

JavaScript是近几年普及很快的语言,其初衷是作为客户端的脚本语言,但是现在也用于服务器端应用程序(node.js),并成为了上述的移动跨平台工具的一部分。它可能成为解决我们问题的跨平台语言吗?

所有的移动跨平台都能在web-browser视图里执行JacaScript脚本 (WebViews),并且WebView的API通常都呈现在开发者眼前。

我们在JavaScript中需要的最少功能如下:

  • 执行函数

  • 调用脚本

  • 计算全局变量和返回的结果

  • 执行回调 (到本地代码)

我们来单独地探讨各个平台。

在Android中,WebView能执行脚本串。回调到Java代码是JavaScript实现的,它注解(用 @JavascriptInterface)可以调用的Java类中的确定方法,并添加这些类的实例到WebView的JavaScript全局作用域的 引用(用addJavascriptInterface()方法)。然而计算变量或者函数调用,并不是这么简单,因为没有一种像脚本一样直接计算的方法。 应对这个问题的唯一措施是向JavaScript传递一个回调函数,这样当结果计算出来之后,回调函数被调用,传递结果到Java的方法作为参数。详见这里

在iOS中,UIWebView能执行脚本串。与Android不同的是,IOS中可以计算全 局变量和函数调用(用stringByEvaluatingJavaScriptFromString:),,但是要作为字符串返回,因此当结果不是字符 串的时候要做一些适当的转换。然而,回调函数却不像Android中的那样简单,这是因为在UIWebView中没有这种机制。从JavaScript中 调用Objective-C的唯一应对方案,是试图在JavaScript中打开一个带有定制协议的URL(例如skycallback://) ,并在Objective-C中捕捉这一事件,然后解析URL,看协议中是否含有回调协议的名称,或者解析URL的资源路径的字符串值,或者计算存放结果 的全局变量。详见这里answer.

你可以看到,JavaScript和本地代码之间的交互是十分困难的,并因平台而已,而且当代码量的增长,这种交互很容易导致bug,并不可避免地变得难以维护。因此,这个选项被抛弃掉。

选项5 - JS引擎中的Javascript

让JavaScript运行在一个独立的JavaScript引擎中也可以工作。

和Web视图的方式相比,Javascript直接与Js引擎交互更为直接。但不幸的是,纯净的JS引擎缺少网络功能。JS中处理Http请求的 XMLHttpRequest对象无效,原因是它是web浏览器的一部分而并非严格的JavaScript规范。因此,通过代理特定平台(胶水)代码的网 络功能,一个与众不同的架构应运而生。虽然这使得有些事情变得错综复杂,但是我们特别感兴趣的是可以开发跨平台的JavaScript库。下面是它的工作 原理:

在 iOS 中,JavaScriptCore 引擎通过极佳的 JavaScriptCore 框架被使用。这个框架在 iOS 7 中被引入,并且它在几秒钟之内就可以很容易的可以集成到应用中,就如你处理任何 Cocoa Touch framework 一样。它的 API 非常简单,所需的绑定代码也很简洁。

在 Android 中,事情还是有些复杂,因为没有 JavaScript 引擎,所以我们必须手工嵌入一个。两个 JavaScript 引擎都可以被嵌入成功,Rhino 和 V8。Rhino 用 Java 编写,所以它很容易嵌入,并且它仅仅增加了 2.6MB 的应用程序大小。它由 Mozilla 基金会开发,但它的开发现在有一段时间不活跃了。 V8 嵌入难度要大些,它用 C++ 编写。因此,必须使用 Android NDK 和 JNI 来供 Java 与其交互,又增加了一个转换层(Java<->C++<->JavaScript,而非 Java<->JavaScript)。此外,应用程序大小增加了 7.1MB,这对于一些应用程序并不是可以忽略的。不管怎样,它的开发非常活跃

跨平台库以Http请求处理为存根由JavaScript开发。这个存根一开始工作为一个占位符,而在库载入到JavaScript引擎中后会被重新写入。它被一个调用实现这个请求的本地方法(特定平台)的方法替换。

“JavaScript引擎中的JavaScript"解决方案的全部说明将出现在这个系列文章的第三部分,这些文章将在接下来的几周内发布。

结论

虽然研究了很多工具和技术,但是其中只有两个可以工作。在一方面,C + +的解决方案是一种广泛使用的,可靠的,灵活的解决方案,但在Java中手动垃圾收集(提出了一种解决方法的)的一个显著的缺点。另一方 面,JavaScript的解决方案更容易实现,但在复杂的体系结构缺少功能,并且是依赖于并非积极开发中的Rhino,或在V8这对应用程序大小有显著 影响。如果您使用这些方法中的一种,请对这些缺点统筹考虑,谨慎行事。

一些项目看上去很有前途,将来值得重复查看:

  • Corona

  • MoSync

  • J2ObjC

  • Appcelerator Hyperloop

  • Nashorn (Oracle用Java重写的Javascript引擎)

责任编辑:徐川 来源: oschina
相关推荐

2011-06-14 10:05:41

UDEAndroidJ2ME(K-Java

2012-03-16 13:43:29

2010-10-09 15:01:27

PhoneGapiPhoneAndroid

2011-07-06 11:16:32

Unity3DCocos2dSparrow Fra

2015-03-28 19:18:47

线下公开课51CTO沙龙MDSA

2014-07-08 09:37:28

跨平台Webhtml5

2010-11-11 09:04:05

跨平台移动开发

2013-05-27 09:47:33

Java开发Java跨平台

2022-11-21 07:57:56

cmake工程模板

2014-03-24 10:30:38

卢建晖跨平台开发

2015-03-17 09:59:41

跨平台

2015-01-14 09:41:28

跨平台移动应用Linux开发

2014-12-08 10:03:47

IonicHybrid混合应用

2013-01-23 09:15:58

SaaS移动开发平台

2012-08-22 13:34:30

移动开发跨平台

2015-05-13 10:09:39

移动跨平台开发

2013-07-04 10:06:32

AppCan

2020-09-08 11:21:48

SQL生成器跨库

2020-08-06 08:17:52

FaaS平台Serverless
点赞
收藏

51CTO技术栈公众号