点击上方的终端研发部,右上角选择“设为星标”
每日早10点半,技术文章准时送上
公众号后台回复“学习”,获取作者独家秘制精品资料

往期文章
作者:吴多益
介绍
之前出现的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,所以不管什么问题都只能抱怨它了。
那么问题在哪呢?在我看来有三方面的问题:
1、早期浏览器实现比较差,没有进行优化
2、CSS过于复杂,计算起来更耗时
3、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标准需要漫长的编写和评审过程,增加4000 API我这辈子是等不到了,即便是Cordova这样自己封装也忙不过来,所以为了更好地使用系统新功能,写Native代码是必须的。
P.S. 虽然前面提到HTML/CSS过于复杂导致性能问题,但其实这正是Web最大的优势所在,因为Web最初的目的就是显示文档,如果你想显示丰富的图文排版,虽然iOS/Android都有富文本组件,但比起CSS差太远了,所以在很多Native应用中是不可避免要嵌Web的。
代码转换流
前面提到写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代码,看起来就像汇编。
XMLVM_ENTER_METHOD("org.xmlvm.tutorial.ios.helloworld.portrait.HelloWorld", "didFinishLaunchingWithOptions", "?")XMLVMElem_r0;XMLVMElem_r1;XMLVMElem_r2;XMLVMElem_r3;XMLVMElem_r4;XMLVMElem_r5;XMLVMElem_r6;XMLVMElem_r7; _r5.o=me; _r6.o=n1; _r7.o=n2; _r4.i=0; _r0.o=org_xmlvm_iphone_UIScreen_mainScreen__();XMLVM_CHECK_NPE(0) _r0.o=org_xmlvm_iphone_UIScreen_getApplicationFrame__(_r0.o); _r1.o= __NEW_org_xmlvm_iphone_UIWindow();XMLVM_CHECK_NPE(1) ...
在我看来这个方案相当不靠谱,万一生成的代码有问题基本没法修改,也没法调试代码,所以不推荐。
Flutter
就在 React Native 的人气不断下跌的时候,Google 在一个恰到好处的时机,推出了一套跨平台方案:Flutter,将人们的目光再次聚集到跨平台开发上面。

Flutter 使用 Dart 这门较为冷门的语言来做开发,底基引擎主要由 Skia 和 Dart runtime 构成。
Flutter 通过 Skia 和各平台的底层图形库对接,同时提供丰富的基于 Skia 的控件,来实现跨平台的开发。
React Native 采用的方式是封装各个平台的应用层接口,而 Flutter 则直接打造了一套跨平台的应用层的开发套件。
对这两种不同的方式,我们可以有一个直觉上的判断,Flutter 在性能上是要优于 React Native 的。
因为 Flutter 的这种实现方式,其实早已被大量并广泛的使用了,最明显的例子就是游戏引擎。
种种对比和迹象表明,似乎 Flutter 是一个比 React Native 更好的跨平台方案。
目前 Flutter 仍旧处于一个上升的势头,也有如阿里巴巴这种大厂给 Flutter 背书,颇具野心的底基框架让开发者有理由相信,只要投入足够的人力,Flutter 可以做到和原生开发一样好。
然而,Flutter 的缺点,也是源于它自行打造了框架,在很多平台特性上,诸如密码管理,选择光标等,Flutter 目前并不能支持,未来能否支持也要打上大大的问号,平台的特性可能和原生组件深度绑定,且目前没有其它接口,所以 Flutter 在现在这种状态下,只能是放弃这些特性的支持。
需要提一点的是,React Native 在这方面没有太大的问题。
尽管有一些问题,不过 Flutter 表现出的潜力,还是让人们觉得,这是一个值得一试的方案,只要 Google 给予足够的支持。
所以 Google 会吗?
小结
虽然代码转换这种方式风险小,但我觉得对于很多小APP来说共享不了多少代码,因为这类应用大多数围绕UI来开发的,大部分代码都和UI耦合,所以公共部分不多。
事实上就是,没有一个完美的方案,任何方案都有利弊和取舍。想使用 Javascript 开发应用,那么就使用 React Native;想构建高性能的跨平台应用,Flutter 是个不错的选择;想最大化平台特性,那自然是原生的开发方式。
阅读更多
相信自己,没有做不到的,只有想不到的
在这里获得的不仅仅是技术!


喜欢就给个“在看”