查看原文
其他

跨平台框架这么多, 老夫无可奈何!

吴多益 终端研发部 2022-08-26

点击上方的终端研发部右上角选择设为星标

每日早10点半,技术文章准时送上

公众号后台回复“学习”,获取作者独家秘制精品资料


往期文章

重磅!Vue 3.0源代码公布后,究竟有哪些变更?

使用Vue开发微信小程序:mpvue框架

混合开发框架的对比,Flutter更胜一筹?

2019最新Vue开发指南,值得收藏!

混合开发框架最全对比,为什么我更推荐Flutter?

100万条数据遍历,lambda会比for循环快吗

作者:吴多益

介绍

之前出现的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 是个不错的选择;想最大化平台特性,那自然是原生的开发方式。

阅读更多

apk优化的一些奇淫技巧

Go 和 Android 集成实战

100万条数据遍历,lambda会比for循环快吗

重磅!阿里内部偷师Android的开发规范文档

面试BAT大厂,可少不了这些题目!

重磅!Vue 3.0源代码公布后,究竟有哪些变更?

相信自己,没有做不到的,只有想不到的

在这里获得的不仅仅是技术!

喜欢就给个“在看

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存