县科协微网站建设,三网合一 网站建设,广州微网站建设平台,广州安全教育平台app下载介绍
最近出现的 React Native 再次让跨平台移动端开发这个话题火起来了#xff0c;曾经大家以为在手机上可以像桌面那样通过 Web 技术来实现跨平台开发#xff0c;却大多因为性能或功能问题而放弃#xff0c;不得不针对不同平台开发多个版本。
但这并没有阻止人们对跨平台…
介绍
最近出现的 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 标准需要漫长的编写和评审过程增加 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)
...在我看来这个方案相当不靠谱万一生成的代码有问题基本没法修改也没法调试代码所以不推荐。
小结
虽然代码转换这种方式风险小但我觉得对于很多小 APP 来说共享不了多少代码因为这类应用大多数围绕 UI 来开发的大部分代码都和 UI 耦合所以公共部分不多。
在目前的所有具体方案中只有 j2objc 可以尝试其它都不成熟。
编译流
编译流比前面的代码转换更进一步它直接将某个语言编译为普通平台下的二进制文件这种做法有明显的优缺点
优点 可以重用一些实现很复杂的代码比如之前用 C 实现的游戏引擎重写一遍成本太高编译后的代码反编译困难或许性能会好些具体要看实现缺点 如果这个工具本身有 Bug 或性能问题定位和修改成本会很高编译后体积不小尤其是如果要支持 ARMv8 和 x86 的话
接下来我们通过区分不同语言来介绍这个流派下的各种方案。
C 类
C 是最常见的选择因为目前 Android、iOS 和 Windows Phone 都提供了 C 开发的支持它通常有三种做法
只用 C 实现非界面部分这是官方比较推崇的方案目前有很多应用是这么做的比如 Mailbox 和 Microsoft Office。使用 2D 图形库来自己绘制界面这种做法在桌面比较常见因为很多界面都有个性化需求但在移动端用得还不多。使用 OpenGL 来绘制界面常见于游戏中。
使用 C 实现非界面部分比较常见所以这里就不重复介绍了除了能提升性能和共用代码还有人使用这种方式来隐藏一些关键代码比如密钥如果你不知道如何构建这样的跨平台项目可以参考 Dropbox 开源的 libmx3 项目它还内嵌了 json 和 sqlite 库并通过调用系统库来实现对简单 HTTP、EventLoop 及创建线程的支持。
而如果要用 C 实现界面部分在 iOS 和 Windows Phone 下可以分别使用 C 的超集 Objective-C 和 C/CX所以还比较容易但在 Android 下问题就比较麻烦了主要原因是 Android 的界面绝大部分是 Java 实现的所以用 C 开发界面最大的挑战是如何支持 Android这有两种做法通过 JNI 调用系统提供的 Java 方法或者自己画 UI。
第一种做法虽然可行但代码太冗余了比如一个简单的函数调用需要写那么多代码
JNIEnv* env;
jclass testClass (*env)-FindClass(env, com/your/package/name/Test); // get Class
jmethodID constructor (*env)-GetMethodID(env, cls, init, ()V);
jobject testObject (*env)-NewObject(env, testClass, constructor);
methodID callFromCpp (*env)-GetMethodID(env, testClass, callFromCpp, ()V); //get methodid
(*env)-CallVoidMethod(env, testObject, callFromCpp);那自己画 UI 是否会更方便点比如 JUCE 和 QT 就是自己画的我们来看看 QT 的效果 看起来很不错是吧不过在 Android 5 下就悲剧了很多效果都没出来比如按钮没有涟漪效果甚至边框都没了根本原因在于它是通过 Qt Quick Controls 的自定义样式来模拟的而不是使用系统 UI 组件因此它享受不到系统升级自动带来的界面优化只能自己再实现一遍工作量不小。
反而如果最开始用的是 Android 原生组件就什么都不需要做而且还能用新的 AppCompat 库来在 Android 5 以下实现 Material Design 效果。
最后一种做法是使用 OpenGL 来绘制界面因为 EGLOpenGL 本身就是跨平台所以基于它来实现会很方便目前大多数跨平台游戏底层都是这么做的。
既然可以基于 OpenGL 来开发跨平台游戏是否能用它来实现界面当然是可行的而且 Android 4 的界面就是基于 OpenGL 的不过它并不是只用 OpenGL 的 API那样是不现实的因为 OpenGL API 最初设计并不是为了画 2D 图形的所以连画个圆形都没有直接的方法因此 Android 4 中是通过 Skia 将路径转换为位置数组或纹理然后再交给 OpenGL 渲染的。
然而要完全实现一遍 Android 的 UI 架构工作量不小以下是其中部分相关代码的代码量
路径代码行数frameworks/base/core/java/android/widget/65622frameworks/base/core/java/android/view/49150frameworks/base/libs/hwui/16375frameworks/base/graphics/java/android/graphics/18197
其中光是文字渲染就非常复杂如果你觉得简单那只能说明你没看过这个世界有多大或许你知道中文有编码问题、英语有连字符(hyphen)折行但你是否知道繁体中文有竖排版、阿拉伯文是从右到左的、日语有平假名注音(ルビ)、印度语有元音附标文字(abugida አቡጊዳ)……
而相比之下如果每个平台单独开发界面看似工作量不小但目前在各个平台下都会有良好的官方支持相关工具和文档都很完善所以其实成本没那么高而且可以给用户和系统风格保持一致的良好体验所以我认为对于大多数应用来说自己画 UI 是很不划算的。
不过也有特例对于 UI 比较独特的应用来说自己画也是有好处的除了更灵活的控制它还能使得不同平台下风格统一这在桌面应用中很常见比如 Windows 下你会发现几乎每个必备软件的 UI 都不太一样而且好多都有换肤功能在这种情况下很适合自己画 UI。
Xamarin
Xamarin 可以使用 C# 来开发 Android 及 iOS 应用它是从 Mono 发展而来的目前看起来商业运作得不错相关工具及文档都挺健全。
因为它在 iOS 下是以 AOT 的方式编译为二进制文件的所以把它归到编译流来讨论其实它在 Android 是内嵌了 Mono 虚拟机 来实现的因此需要装一个 17M 的运行环境。
在 UI 方面它可以通过调用系统 API 来使用系统内置的界面组件或者基于 Xamarin.Forms 开发定制要求不高的跨平台 UI。
对于熟悉 C# 的团队来说这还真是一个看起来很不错的但这种方案最大的问题就是相关资料不足遇到问题很可能搜不到解决方案不过由于时间关系我并没有仔细研究推荐看看这篇文章其中谈到它的优缺点是
优点 开发 app 所需的基本功能全部都有有商业支持而且这个项目对 Windows Phone 很有利微软会大力支持缺点 如果深入后会发现功能缺失尤其是定制 UI因为未开源使得遇到问题时不知道如何修复Xamarin 本身有些 Bug相关资源太少没有原生平台那么多第三方库Xamarin studio 比起 Xcode 和 Android Studio 在功能上还有很大差距
Objective-C 编译为 Windows Phone
微软知道自己的 Windows Phone 太非主流所以很懂事地推出了将 Objective-C 项目编译到 Windows Phone 上运行的工具目前这个工具的相关资料很少鉴于 Visual Studio 支持 Clang所以极有可能是使用 Clang 的前端来编译这样最大的好处是以后支持 Swift 会很方便因此我归到编译流。
而对于 Android 的支持微软应该使用了虚拟机的方式所以放到下个章节介绍。
RoboVM
RoboVM 可以将 Java 字节码编译为可在 iOS 下运行的机器码这有点类似 GCJ但它的具体实现是先使用 Soot 将字节码编译为 LLVM IR然后通过 LLVM 的编译器编译成不同平台下的二进制文件。
比如简单的 new UITextField(new CGRect(44, 32, 232, 31)) 最后会变如下的机器码(x86)
call imp___jump_table__[j]org.robovm.apple.uikit.UITextField[allocator][clinit]
mov esi, eax
mov dword [ss:esp], ebx
call imp___jump_table__[j]org.robovm.apple.coregraphics.CGRect[allocator][clinit]
mov edi, eax
mov dword [ss:esp0x4], edi
mov dword [ss:esp], ebx
mov dword [ss:esp0xc], 0x40460000
...基于字节码编译的好处是可以支持各种在 JVM 上构建的语言比如 Scala、Kotlin、Clojure 等。
在运行环境上它使用的 GC 和 GCJ 一样都是 Boehm GC这是一个保守 GC会有内存泄露问题尽管官方说已经优化过了影响不大。
在 UI 的支持方面它和 Xamarin 挺像可以直接用 Java 调用系统接口来创建界面最近支持 Interface Builder 了比如上面的示例就是。另外还号称能使用 JavaFX这样就能在 iOS 和 Android 上使用同一套 UI 了不过目前看起来很不靠谱。
在我看来 RoboVM 目前最大的用途就是使用 libGDX 开发游戏了尽管在功能上远不如 Cocos2d-x尤其是场景及对象管理但不管怎么说用 Java 比 C 还是方便很多别跟我说没人用 Java 做游戏价值 25 亿美元的 Minecraft 就是不过本文主要关心的是 UI 开发所以这方面的话题就不深入讨论了
RoboVM 和 Xamarin 很像但 RoboVM 风险会小些因为它只需要把 iOS 支持好就行了对优先开发 Android 版本的团队挺适用但目前官方文档太少了而且不清楚 RoboVM 在 iOS 上的性能和稳定性怎样。
Swift - Apportable/Silver
apportable 可以直接将 Swift/Objective-C 编译为机器码但它官网的成功案例全部都是游戏所以用这个来做 APP 感觉很不靠谱。
所以后来它又推出了 Tengu 这个专门针对 APP 开发的工具它的比起之前的方案更灵活些本质上有点类似 C 公共库的方案只不过语言变成了 Swift/Objective-C使用 Swift/Objective-C 来编译生成跨平台的 SO 文件提供给 Android 调用。
另一个类似的是 Silver不过目前没正式发布它不仅支持 Swift还支持 C# 和自创的 Oxygene 语言看起来像 Pascal在界面方面它还有个跨平台非 UI 库 Sugar然而目前 Star 数只有 17太非主流了所以实在懒得研究它。
使用 Swift 编译为 SO 给 Android 用虽然可行但目前相关工具都不太成熟所以不推荐使用。
Go
Go 是最近几年很火的后端服务开发语言它语法简单且高性能目前在国内有不少用户。
Go 从 1.4 版本开始支持开发 Android 应用并将在 1.5 版本支持 iOS不过前只能调用很少 的 API比如 OpenGL 等所以只能用来开发游戏但我感觉并不靠谱现在还有谁直接基于 OpenGL 开发游戏大部分游戏都是基于某个框架的而 Go 在这方面太缺乏了我只看到一个桌面端 Azul3D而且非常不成熟。
因为 Android 的 View 层完全是基于 Java 写的要想用 Go 来写 UI 不可避免要调用 Java 代码而这方面 Go 还没有简便的方式目前 Go 调用外部代码只能使用 cgo通过 cgo 再调用 jni这需要写很多中间代码所以目前 Go 1.4 采用的是类似 RPC 通讯的方式来做从它源码中例子可以看出这种方式有多麻烦性能肯定有不小的损失。
而且 cgo 的实现本身就对性能有损失除了各种无关函数的调用它还会锁定一个 Go 的系统线程这会影响其它 gorountine 的运行如果同时运行太多外部调用甚至会导致所有 gorountine 等待。
这个问题的根源在于 Go 的栈是可以自动扩充的这种方式有利于创建无数 gorountine但却也导致了无法直接调用 C 编译后的函数需要进行栈切换。
所以使用 Go 开发跨平台移动端应用目前不靠谱。
话说 Rust 没有 Go 的性能它调用 C 函数是没有性能损耗的但目前 Rust 还没提供对 iOS/Android 的官方支持尽管有人还是尝试过是可行的但现在还不稳定从 Rust 语言本身的设计来看它挺适合取代 C 来做这种跨平台公共代码但它的缺点是语法复杂会吓跑很多开发者。
Xojo
我之前一直以为 BASIC 挂了没想到还有这么一个特例Xojo 使用的就是 BASIC它有看起来很强大的 IDE让人感觉像是在用 VisualBasic。
它的定位应该是给小朋友或业余开发者用的因为似乎看起来学习成本低但我不这么认为因为用得人少反而网上资料会很少所以恐怕成本会更高。
因为时间关系以及对 BASIC 无爱我并没有怎么研究它。
小结
从目前分析的情况看C 是比较稳妥的选择但它对团队成员有要求如果大家都没写过 C可以试试 Xamrin 或 RoboVM。
虚拟机流
除了编译为不同平台下的二进制文件还有另一种常见做法是通过虚拟机来支持跨平台运行比如 JavaScript 和 Lua 都是天生的内嵌语言所以在这个流派中很多方案都使用了这两个语言。
不过虚拟机流会遇到两个问题一个是性能损耗另一个是虚拟机本身也会占不小的体积。
Java 系
说到跨平台虚拟机大家都会想到 Java因为这个语言一开始就是为了跨平台设计的Sun 的 J2ME 早在 1998 年就有了在 iPhone 出来前的手机上很多小游戏都是基于 J2ME 开发的这个项目至今还活着能运行在 Raspberry Pi 上。
前面提到微软提供了将 Objective-C 编译在 Windows Phone 上运行的工具在对 Android 的支持上我没找到的详细资料所以就暂时认为它是虚拟机的方式从 Astoria 项目的介绍上看它做得非常完善不仅能支持 NDK 中的 C还实现了 Java 的 debug 接口使得可以直接用 Android Studio 等 IDE 来调试整个开发体验和在 Android 手机上几乎没区别。
另外 BlackBerry 10 也是通过内嵌虚拟机来支持直接运行 Android 应用不过据说比较卡。
不过前面提到 C# 和 Java 在 iOS 端的方案都是通过 AOT 的方式实现的目前还没见到有 Java 虚拟机的方案我想主要原因是 iOS 的限制普通 app 不能调用 mmap、mprotect所以无法使用 JIT 来优化性能如果 iOS 开放或许哪天有人开发一个像微软那样能直接在 iOS 上运行 Android 应用的虚拟机就不需要跨平台开发了大家只需要学 Android 开发就够了。。。
Titanium/Hyperloop
Titanium 应该不少人听过它和 PhoneGap 几乎是同时期的著名跨平台方案和 PhoneGap 最大的区别是它的界面没有使用 HTML/CSS而是自己设计了一套基于 XML 的 UI 框架 Alloy代码类似下面这个样子
app/styles/index.tss
.container: {backgroundColor:white
},
// This is applied to all Labels in the view
Label: {width: Ti.UI.SIZE,height: Ti.UI.SIZE,color: #000, // blacktransform: Alloy.Globals.rotateLeft // value is defined in the alloy.js file
},
// This is only applied to an element with the id attribute assigned to label
#label: {color: #999 /* gray */
}app/views/index.xml
AlloyWindow classcontainerLabel idlabel onClickdoClickHello, World/Label/Window
/Alloy前面我们说过由于 CSS 的过于灵活拖累了浏览器的性能那是否自己建立一套 UI 机制会更靠谱呢尽管这么做对性能确实有好处然而它又带来了学习成本问题做简单的界面问题不大一旦要深入定制开发就会发现相关资料太少所以还是不靠谱。
Titanium 还提供了一套跨平台的 API 来方便调用这么做是它的优点更是缺点尤其是下面三个问题
API 有限因为这是由 Titanium 提供的它肯定会比官方 API 少且有延迟Titanium 是肯定跟不过来的相关资料及社区有限比起 Android/iOS 差远了遇到问题都不知道去哪找答案缺乏第三方库第三方库肯定不会专门为 Titanium 提供一个版本所以不管用什么都得自己封装
Titanium 也意识到了这个问题所以目前在开发下一代的解决方案 Hyperloop它可以将 JavaScript 编译为原生代码这样的好处是调用原生 API 会比较方便比如它的 iOS 是这样写的
import(UIKit);
import(CoreGraphics);
var view new UIView();
view.frame CGRectMake(0, 0, 100, 100);这个方案和之前的说的 Xamarin 很相似基本上等于将 Objective-C 翻译为 JavaScript 后的样子意味着你可以对着 Apple 的官方文档开发不过如果发现某些 Objective-C 语法发现不知道对应的 JavaScript 怎么写时就悲剧了只有自己摸索。
但从 Github 上的提交历史看这项目都快开发两年了但至今仍然是试验阶段从更新频率来看最近一年只提交了 8 次所以恐怕是要弃坑了非常不靠谱。
因此我认为 Titanium/Hyperloop 都非常不靠谱不推荐使用。
NativeScript
之前说到 Titanium 自定义 API 带来的各种问题于是就有人换了个思路比如前段时间推出的 NativeScript它的方法说白了就是用工具来自动生成 wrapper API和系统 API 保持一致。
有了这个自动生成 wrapper 的工具它就能方便基于系统 API 来开发跨平台组件以简单的 Button 为例源码在 cross-platform-modules/ui/button 中它在 Android 下是这样实现的TypeScript 省略了很多代码
export class Button extends common.Button {private _android: android.widget.Button;private _isPressed: boolean;public _createUI() {var that new WeakRef(this);this._android new android.widget.Button(this._context);this._android.setOnClickListener(new android.view.View.OnClickListener({get owner() {return that.get();},onClick: function (v) {if (this.owner) {this.owner._emit(common.knownEvents.tap);}}}));}
}而在 iOS 下是这样实现的省略了很多代码
export class Button extends common.Button {private _ios: UIButton;private _tapHandler: NSObject;private _stateChangedHandler: stateChanged.ControlStateChangeListener;constructor() {super();this._ios UIButton.buttonWithType(UIButtonType.UIButtonTypeSystem);this._tapHandler TapHandlerImpl.new().initWithOwner(this);this._ios.addTargetActionForControlEvents(this._tapHandler, tap, UIControlEvents.UIControlEventTouchUpInside);this._stateChangedHandler new stateChanged.ControlStateChangeListener(this._ios, (s: string) {this._goToVisualState(s);});}get ios(): UIButton {return this._ios;}
}可以看到用法和官方 SDK 中的调用方式是一样的只不过语言换成了 JavaScript并且写法看起来比较诡异罢了风格类似前面的 Hyperloop 类似所以也同样会有语法转换的问题。
这么做最大的好处就是能完整支持所有系统 API对于第三方库也能很好支持但它目前最大缺点是生成的文件体积过大即便什么都不做生成的 apk 文件也有 8.4 MB因为它将所有 API binding 都生成了而且这也导致在 Android 下首次打开速度很慢。
从底层实现上看NativeScript 在 Android 下内嵌了 V8而在 iOS 下内嵌了自己编译的 JavaScriptCore这意味着没有 JIT 优化具体原因前面提到了这样的好处是能调用更底层的 API也避免了不同操作系统版本下 JS 引擎不一致带来的问题但后果是生成文件的体积变大和在 iOS 下性能不如 WKWebView。 WKWebView 是基于多进程实现的它在 iOS 的白名单中所以能支持 JIT。 它的使用体验很不错做到了一键编译运行而且还有 MVVM 的支持能进行数据双向绑定。
在我看来 NativeScript 和 Titanium 都有个很大的缺点那就是排它性太强如果你要用这两个方案就得完整基于它们进行开发不能在某些 View 下进行尝试也不支持直接嵌入第三方 View有没有方案能很好地解决这两个问题有那就是我们接下来要介绍的 React Native。
React Native
关于 React Native 目前网上有很多讨论了知乎上也有不少回答尽管有些回答从底层实现角度看并不准确但大部分关键点倒是都提到了。
鉴于我不喜欢重复别人说过的话这里就聊点别的。
React Native 的思路简单来说就是在不同平台下使用平台自带的 UI 组件这个思路并不新奇十几年前的 SWT 就是这么做的。
从团队上看Facebook 的 iOS 团队中不少成员是来自 Apple 的比如 Paper 团队的经理及其中不少成员都是因为 iOS 不开源所以从 Apple 中出来的开发者还是有优势的比如前 Apple 开发者搞出来的 Duet 就秒杀了市面上所有其他方案而且从 Facebook 在 iOS 上开源的项目看他们在 iOS 方面的经验和技术都不错所以从团队角度看他们做出来的东西不会太差。
在做 React Native 方案的同时其实 Facebook 还在做一个 Objective-C 上类似 React 的框架 ComponentKit以下是它的代码示例
implementation ArticleComponent (instancetype)newWithArticle:(ArticleModel *)article
{return [super newWithComponent:[CKStackLayoutComponentnewWithView:{}size:{}style:{.direction CKStackLayoutDirectionVertical,}children:{{[HeaderComponent newWithArticle:article]},{[MessageComponent newWithMessage:article.message]},{[FooterComponent newWithFooter:article.footer]},}];
}end它的可读性比 JSX 中的 XML 差了不少而且随着大家逐步接受 Swift这种基于 Objective-C 的方案恐怕没几年就过时了所以 Facebook 押宝 React 是比较正确的。
我看到有人说这是 Facebook 回归 H5但其实 React Native 和 Web 扯不上太多关系我所理解的 Web 是指 W3C 定义的那些规范比如 HTML、CSS、DOM而 React Native 主要是借鉴了 CSS 中的 Flexbox 写法还有 navigator、XMLHttpRequest 等几个简单的 API更别说完全没有 Web 的开放性所以 React Native 和 HTML 5 完全不是一回事。
Facebook Groups 的 iOS 版本很大一部分基于 React Native 开发其中用到了不少内部通过组件比如 ReactGraphQL这里我就八卦一下它GraphQL 这是一个结构化数据查询的语法就像 MongoDB 查询语法那样查询 JSON 数据不过它并不是一种文档型数据库而只是一个中间层具体的数据源可以连其它数据库它想取代的应该是 RESTful 那样的前后端简单 HTTP 协议让前端更方便的获取数据据说将会开源看起来打算用 Node 实现。 写文章拖时间太长的问题就是这期间会发生很多事情比如 GraphQL 在我开始写的时候外界都不知道所以需要八卦一下结果现在官方已经宣布了不过官方并没提到我说的那个 Node 实现它目前还在悄悄开发阶段 React Native 的官方视频中说它能做到 App 内实时更新其实这是 Apple 明文禁止的App Store Review Guidelines 中的 2.7要做得低调。 评论中有人提到 Apple 居然在 iOS 8.2 中改条款了可以下载执行 JavaScript而且 UIKit 的作者都觉得 React Native 很赞 我比较喜欢的是 React Native 中用到了 Flow它支持定义函数参数的类型极大提升了代码可读性另外还能使用 ES6 的语法比如 class 关键字等。
React Native 比传统 Objective-C 和 UIView 的学习成本低多了熟悉 JavaScript 的开发者应该半天内就能写个使用标准 UI 的界面而且用 XMLCSS 画界面也远比 UIView 中用 Frame 进行手工布局更易读我没用过 Storyboards它虽然看起来直观但多人编辑很容易冲突感兴趣可以抽空看看这个详细的入门教程亲自动手试试就能体会到了Command R 更新代码感觉很神奇。
它目前已经有组件仓库了而且在 github 上都有 500 多仓库了其中有 sqlite、Camera 等原生组件随着这些第三方组件的完善基于 React Native 开发越来越不需要写原生代码了。
不过坏消息是 React Native 的 Android 版本还要等半年这可以理解因为在 Android 上问题要复杂得多有 Dalvik/ART 拦在中间使得交互起来很麻烦。
NativeScript 和 React Native 在侧重点上有很大的不同使得这两个产品目前走向了不同的方向
React Native 要解决的是开发效率问题它并没指望完全取代 Native 开发它的 rootView 继承自 UIView所以可以在部分 View 是使用很方便混着不需要重写整个 app而且混用的时候还需要显示地将 API 暴露给 JavaScriptNativeScript 则像是 Titanium 那样企图完全使用 JavaScript 开发将所有系统 API 都暴露给了 JavaScript让 JavaScript 语言默认就拥有 Native 语言的各种能力然后再次基础上来开发
方向的不同导致这两个产品将会有不同的结局我认为 React Native 肯定会完胜 NativeScript因为它的使用风险要小很多你可以随时将部分 View 使用 React Native 来试验遇到问题就改回 Native 实现风险可控而用 NativeScript 就不行了这导致大家在技术选型的时候不敢使用 NativeScript。
话说 Angular 团队看到 React Native 后表示不淡定了于是开始重新设计 Angular 2 的展现架构将现有的 Render 层独立出来以便于做到像 React 那样适应不同的运行环境可以运行在 NativeScript 上。
综合来看我觉得 React Native 很值得尝试而且风险也不高。
游戏引擎中的脚本
游戏引擎大多都能跨平台为了提升开发效率不少引擎还内嵌了对脚本支持比如
Ejecta它实现了 Canvas 及 Audio 的 API可以开发简单的游戏但目前还不支持 AndroidCocoonJS实现了 WebGL 的 API可以运行 Three.js 写的游戏Unreal Engine 3可以使用 UnrealScript 来开发这个语言的语法很像 JavaCocos2d-jsCocos2d-x 的 JavaScript binding它内部使用的 JS 引擎是 SpiderMonkeyUnity 3D可以使用 C# 或 JavaScript 开发游戏逻辑Corona使用 Lua 来开发…
目前这种方式只有 Unity 3D 发展比较好Cocos2d-JS 据说还行有些小游戏在使用Corona 感觉比较非主流虽然它也支持简单的按钮等界面元素但用来写 APP 我不看好因为不开源所以没研究目前看来最大的好处似乎是虚拟机体积小内嵌版本官方号称只有 1.4M这是 Lua 引擎比较大的优势。
而剩下的 3 个都基本上挂了Ejecta 至今还不支持 AndroidCocoonJS 转型为类似 Crosswalk 的 WebView 方案而 Unreal Engine 4 开始不再支持 UnrealScript而是转向了使用 C 开发感兴趣可以围观一下 Epic 创始人解释为什么要这么做。
当然这些游戏引擎都不适合用来做 APP一方面是会遇到前面提到的界面绘制问题另一方面游戏引擎的实现一般都要不断重绘这肯定比普通 App 更耗电很容易被用户发现后怒删。
Adobe AIR
从我周边了解到的情况看几乎所有人都以为 Flash 彻底放弃移动端了不得不说 Adobe 的宣传真是失策明明只是放弃移动浏览器端插件Flash 还是可以在 iOS 下运行的好不好那就是 Adobe AIR对于熟悉 ActionScript 的团队来说这是一种挺好的跨平台游戏开发解决方案国内游戏公司之前有用现在还有没人用我就不知道了不过考虑到很多不明真相的小朋友都以为 Flash 在移动端挂了所以后备力量肯定严重不足连人都招不到其它就别想了。 评论中有人指出在 iOS 下是通过编译实现的看来和 Xamarin RoboVM 很类似。 但开发 APP 方面它同样缺乏好的 UI 库Flex 使用体验很差目前基本上算挂了目前只有 Feathers 还算能看不过主要是给游戏中的 UI 设计的并不适合用来开发 APP。
Dart
Dart 在 Web 基本上失败了于是开始转战移动开发目前有两个思路一个是类似 Lua 那样的嵌入语言来统一公共代码但因为 Dart 虚拟机源自 V8在一开始设计的时候就只有 JIT 而没有解释器甚至连字节码都没有所以它无法在 iOS 下运行于是 Dart 团队又做了个小巧的虚拟机 Fletch它基于传统的字节码解释执行方式来运行目前代码只有 1w 多行和 Lua 一样轻量级。
另一个就是最近比较热门的 Sky这里吐槽一下国内外的媒体我看到的报道都是说 Google 想要用 Dart 取代 Android 下的 Java 开发。。。这个东东确实是 Google 的 Chrome 团队开发的但 Google 是一个很大的公司好不好内部有无数小团队某个小团队并不能代表个 Google如果真是 Google 高层的决定它将会在 Google I/O 大会主题演讲上推出来而不是 Dart Developer Summit 这样非主流的技术分享。 有报道称 Sky 只支持在线应用不支持离线这错得太离谱了人家只是为了演示它的在线更新能力你要想将代码内嵌到 app 里当然是可以的。 Sky 的架构如下图所示它参考了 Chrome依靠一个消息系统来和本地环境进行通讯使得 Dart 的代码和平台无关可以运行在各种平台上。 如果你读过前面的文章那你一定和我一样非常关心一个问题Sky 的 UI 是怎么绘制出来的使用系统还是自己画一开始看 Sky 介绍视频的时候我还以为它底层绘制基于 Chrome因为这个视频的演讲者是 Eric Seidel他是 WebKit 项目中非常有名的开发者早年在 Apple 开发 WebKit2008 年跳槽去了 Chrome 团队但他在演讲中并没有提到 WebView而且演示的时候界面非常像原生 Material Design 效果比如点击有涟漪效果所以我又觉得它是类似 React Native 那样使用原生 UI。
然而当我下载那个应用分析后发现它既没使用 Chrome/WebView 也没使用原生 UI 组件难不成是自己绘制的
从 Sky SDK 的代码上看它其中有非常多 Web 的痕迹比如支持标准的 CSS、很多 DOM API但它编译后的体积非常小libsky_shell.so 只有 8.7 MB我之前尝试精简过 Chrome 内核将 WebRTC 等周边功能删掉也要 22 MB这么小的体积肯定要删 Web 核心功能比如 SVG 和部分 CSS3所以我怀疑它实现了简版的 Chrome 内核渲染。
后来无意间看了一下 Mojo 的代码才证实确实如此原来前面那张图中介绍的 Mojo 其实并不完整Mojo 不仅仅是一个消息系统它是一个简版的 Chrome 内核使用 cloc 统计代码就暴露了 12508 text files.11973 unique files.2299 files ignored.
-----------------------------------------------------------
Language files blank comment code
-----------------------------------------------------------
C 3485 129830 107745 689089
C/C Header 3569 92435 125742 417655
C 266 37462 63659 269220
...C 不包含注释的代码部分就有近 70w 行啊而且一看目录结构就是浓浓的 Chromium 风格至少从技术难度来说绝对秒掉前面所有方案也印证了我前面说过如果有简化版 CSS/HTML 就能很好解决性能问题。
这也让我理解了为什么 Eric 在谈到 Mojo 的时候语焉不详让人误以为仅仅是一个消息系统他要是明确说这是一个精简版 Chrome那得引起多大的误会啊没准会有小编用「Google 宣布开发下一代浏览器内核取代 Blink」这样的标题了。
之前 Dart 决定不将 Dart VM 放到 Chrome 里原来并不是因为被众人反对而死心了而是因为 fork 了一个 Chrome 自己拿来玩了。
综合来看目前 Dart 的这两个方案都非常不成熟Sky 虽然在技术上看很强大但 Dart 语言目前接受度非常低比起它所带来的跨平台优点它的缺点更大比如无法使用第三方 Native UI 库也无法使用第三方 Web UI 库这导致它的社区会非常难发展命中注定非主流真可惜了这帮技术大牛但方向比努力更重要希望他们能尽早醒悟让 Sky 也支持 JavaScript。
我的结论
看到这里估计不少读者晕了有那么多种方案最后到底哪个最适合自己该学哪个这里简单说说我的看法。
如果你只会 JavaScript那目前最好的方案是 React Native有了它你即使不了解 Native 开发也能写出很多中小应用反正多半不会火花太多精力也没意义等万一火了再学 Native 开发也不迟啊。
如果你只会 Java那可以尝试 RoboVM 或 j2objcj2objc 虽然目前更稳定靠谱但它不能像 RoboVM 那样完全用 Java 开发所以你还得学 Objective-C 来写界面而 RoboVM 的缺点就是貌似还不太稳定而且似乎除了游戏以外还没见到比较知名的应用使用而它这种方案注定会比 j2objc 更容易出问题所以你得做好踩坑的心理准备。
如果你只会 C#那唯一的选择就是 Xamarin 了。
如果你只会 Objective-C很杯具目前没有比较靠谱的方案我建议你还是学学 Java 吧多学一门语言没啥坏处。
如果你只会 C可以做做游戏或非 UI 的公共部分我不建议使用 QT 或自己画界面还是学学 Native 开发吧。
如果你只会 Go还别指望用它开发移动端因为目前的实现很低效而且这和 Go 底层的实现机制密切相关导致很难优化所以预计很长一段时间内也不会有改观。
如果你会 Rust说明你很喜欢折腾多半也会前面所有语言自己做决定吧。。。
当然上面都是针对个人的对于团队来说那不用想了肯定用 Native然后混用内嵌的方案比如 Lua、React Native前面那些排它的方案比如 Titanium千万别选会死很惨。
本文涉及到的技术点很多有什么不准确的地方欢迎提出另外可以关注我的微博 weibo.com/nwind 进行交流。
P.S. 本文说的是移动端很多人觉得跨平台从来都不靠谱但其实是有的那就是 Web这个历史上最成功的例子太成功以致于大家都习以为常了大树之下寸草不生它挤掉了其它方案的生存空间十几年前还有 B/S 和 C/S 之争呢 作者nwind (http://wuduoyi.com/) - 我的文章禁止未经许可全文拷贝转载未经许可只能转载 1/10 的内容