聊聊移动端跨平台开发的各种技术
In 未分类 on 2015年05月13日 by view: 1,250
0

介绍

最近出现的 React Native 再次让跨平台移动端开发这个话题火起来了,曾经大家以为在手机上可以像桌面那样通过 Web 技术来实现跨平台开发,却大多因为性能或功能问题而放弃,不得不针对不同平台开发多个版本。

但这并没有阻止人们对跨平台开发技术的探索,毕竟谁不想降低开发成本,一次编写就处处运行呢?除了 React Native,这几年还出现过许多其它解决方案,本文我将会对这些方案进行技术分析,供感兴趣的读者参考。

为了方便讨论,我将它们分为了以下 4 大流派:

  • Web 流:也被称为 Hybrid 技术,它基于 Web 相关技术来实现界面及功能
  • 代码转换流:将某个语言转成 Objective-C、Java 或 C#,然后使用不同平台下的官方工具来开发
  • 编译流:将某个语言编译为二进制文件,生成动态库或打包成 apk/ipa/xap 文件
  • 虚拟机流:通过将某个语言的虚拟机移植到不同平台上来运行

Web 流

Web 流是大家都比较了解的了,比如著名的 PhoneGap/Cordova,它将原生的接口封装后暴露给 JavaScript,可以运行在系统自带的 WebView 中,也可以自己内嵌一个 Chrome 内核

作为这几年争论的热点,网上已经有很多关于它的讨论了,这里我重点聊聊大家最关心的性能问题。

Web 流最常被吐槽的就是性能慢(这里指内嵌 HTML 的性能,不考虑网络加载时间),可为什么慢呢?常见的看法是认为「DOM 很慢」,然而从浏览器实现角度来看,其实 DOM 就是将对文档操作的 API 暴露给了 JavaScript,而 JavaScript 的调用这些 API 后就进入内部的 C++ 实现了,这中间并没有多少性能消耗,所以从理论上来说浏览器的 DOM 肯定比 Android 的「DOM」快,因为 Android 的展现架构大部分功能是用 Java 写的,在实现相同功能的前提下,C++ 不大可能比 Java 慢(某些情况下 JIT 编译优化确实有可能做得更好,但那只是少数情况)。

所以从字面意思上看「DOM 很慢」的说法是错误的,这个看法之所以很普遍,可能是因为大部分人对浏览器实现不了解,只知道浏览器有 DOM,所以不管什么问题都只能抱怨它了。

那么问题在哪呢?在我看来有三方面的问题:

  • 早期浏览器实现比较差,没有进行优化
  • CSS 过于复杂,计算起来更耗时
  • DOM 提供的接口太有限,使得难以进行优化

第一个问题是最关键也是最难解决的,现在说到 Web 性能差主要说的是 Android 下比较差,在 iOS 下已经很流畅了,在 Android 4 之前的 WebView 甚至都没有实现 GPU 加速,每次重绘整个页面,有动画的时候不卡才怪。

浏览器实现的优化可以等 Android 4.4 慢慢普及起来,因为 4.4 以后就使用 Chrome 来渲染了。

而对于最新的浏览器来说,渲染慢的原因就主要是第二个问题:CSS 过于复杂,因为从实现原理上看 Chrome 和 Android View 并没有本质上的差别,但 CSS 太灵活功能太多了,所以计算成本很高,自然就更慢了。

那是不是可以通过简化 CSS 来解决?实际上还真有人这么尝试了,比如 Famo.us,它最大的特色就是不让你写 CSS,只能使用固定的几种布局方法,完全靠 JavaScript 来写界面,所以它能有效避免写出低效的 CSS,从而提升性能。

而对于复杂的界面及手机下常见的超长的 ListView 来说,第三个问题会更突出,因为 DOM 是一个很上层的 API,使得 JavaScript 无法做到像 Native 那样细粒度的控制内存及线程,所以难以进行优化,则在硬件较差的机器上会比较明显。对于这个问题,我们一年前曾经尝试过嵌入原生组件的方式来解决,不过这个方案需要依赖应用端的支持,或许以后浏览器会自带几个优化后的 Web Components 组件,使用这些组件就能很好解决性能问题。

现阶段这三个问题都不好解决,所以有人想干脆不用 HTML/CSS,自己来画界面,比如 React canvas 直接画在 Canvas 上,但在我看来这只是现阶段解决部分问题的方法,在后面的章节我会详细介绍自己画 UI 的各种问题,这里说个历史吧,6 年前浏览器还比较慢的时候,Bespin 就这么干过,后来这个项目被使用 DOM 的 ACE 取代了,目前包括 TextMirror 和 Atom 在内的主流编辑器都是直接使用 DOM,甚至 W3C 有人专门写了篇文章吐槽用 Canvas 做编辑器的种种缺点,所以使用 Canvas 要谨慎。

另外除了 Canvas,还有人以为 WebGL 快,就尝试绘制到 WebGL 上,比如 HTML-GL,但它目前的实现太偷懒了,简单来说就是先用 html2canvas 将 DOM 节点渲染成图片,然后将这个图片作为贴图放在 WebGL 中,这等于将浏览器中用 C++ 写的东东在 JavaScript 里实现了一遍,渲染速度肯定反而更慢,但倒是能用 GLSL 做特效来忽悠人。

硬件加速不等同于「快」,如果你以为硬件加速一定比软件快,那你该抽空学学计算机体系结构了

其实除了性能问题,我认为在 Web 流更严重的问题是功能缺失,比如 iOS 8 就新增 4000+ API,而 Web 标准需要漫长的编写和评审过程,根本赶不上,即便是 Cordova 这样自己封装也忙不过来,所以为了更好地使用系统新功能,写 Native 代码是必须的。

代码转换流

前面提到写 Native 代码是必须的,但不同平台下的官方语言不一样,这会导致同样的逻辑要写两次以上,于是就有人想到了通过代码转换的方式来减少工作量,比如将 Java 转成 Objective-C。

这种方式虽然听起来不是很靠谱,但它却是成本和风险都最小的,因为代码转换后就可以用官方提供的各种工具了,和普通开发区别不大,因此不用担心遇到各种诡异的问题,不过需要注意生成的代码是否可读,不可读的方案就别考虑了。

接下来看看目前存在的几种代码转换方式。

将 Java 转成 Objective-C

j2objc 能将 Java 代码转成 Objective-C,据说 Google 内部就是使用它来降低跨平台开发成本的,比如 Google Inbox 项目就号称通过它共用了 70% 的代码,效果很显著。

可能有人会觉得奇怪,为何 Google 要专门开发一个帮助大家写 Objective-C 的工具?还有媒体说 Google 做了件好事,其实吧,我觉得 Google 这算盘打得不错,因为基本上重要的应用都会同时开发 Android 和 iOS 版本,有了这个工具就意味着,你可以先开发 Android 版本,然后再开发 iOS 版本。。。

既然都有成功案例了,这个方案确实值得尝试,而且关键是会 Java 的人多啊,可以通过它来快速移植代码到 Objective-C 中。

将 Objective-C 转成 Java

除了有 Java 转成 Objective-C,还有 Objective-C 转成 Java 的方案,那就是 MyAppConverter,比起前面的 j2objc,这个工具更有野心,它还打算将 UI 部分也包含进来,从它已转换的列表中可以看到还有 UIKit、CoreGraphics 等组件,使得有些应用可以不改代码就能转成功,不过这点我并不看好,对于大部分应用来说并不现实。

由于目前是收费项目,我没有尝试过,对技术细节也不了解,所以这里不做评价。

将 Java 转成 C#

Mono 提供了一个将 Java 代码转成 C# 的工具 Sharpen,不过似乎用的人不多,Star 才 118,所以看起来不靠谱。

还有 JUniversal 这个工具可以将 Java 转成 C#,但目前它并没有发布公开版本,所以具体情况还待了解,它的一个特色是自带了简单的跨平台库,里面包括文件处理、JSON、HTTP、OAuth 组件,可以基于它来开发可复用的业务逻辑。

比起转成 Objective-C 和 Java 的工具,转成 C# 的这两个工具看起来都非常不成熟,估计是用 Windows Phone 的人少。

将 Haxe 转成其它语言

说到源码转换就不得不提 Haxe 这个奇特的语言,它没有自己的虚拟机或可执行文件编译器,所以只能通过转成其它语言来运行,目前支持转成 Neko(字节码)、Javascript、Actionscript 3、PHP、C++、Java、C# 和 Python,尽管有人实现了转成 Swift 的支持,但还是非官方的,所以要想支持 iOS 开发目前只能通过 Adobe AIR 来运行。

在游戏开发方面做得不错,有个跨平台的游戏引擎 OpenFL 的,最终可以使用 HTML5 Canvas、OpenGL 或 Flash 来进行绘制,OpenFL 的开发体验做得相当不错,同一行代码不需要修改就能编译出不同平台下的可执行文件,因为是通过转成 C++ 方式进行编译的,所以在性能和反编译方面都有优势,可惜目前似乎并不够稳定,不然可以成为 Cocos2d-x 的有利竞品。

在 OpenFL 基础上还有个跨平台的 UI 组件 HaxeUI,但界面风格我觉得特别丑,也就只能在游戏中用了。

所以目前来看 Haxe 做跨平台游戏开发或许可行,但 APP 开发就别指望了,而基于它来共用代码实在就更不靠谱了,因为熟悉它的开发者极少,反而增加成本。

XMLVM

除了前面提到的源码到源码的转换,还有 XMLVM 这种与众不同的方式,它首先将字节码转成一种基于 XML 的中间格式,然后再通过 XSL 来生成不同语言,目前支持生成 C、Objective-C、JavaScript、C#、Python 和 Java。

虽然基于一个中间字节码可以方便支持多语言,然而它也导致生成代码不可读,因为很多语言中的语法糖会在字节码中被抹掉,这是不可逆的,以下是一个简单示例生成的 Objective-C 代码,看起来就像汇编: