官方地址:https://developer.xamarin.com/guides/cross-platform/advanced/razor_html_templates/html
使用Xamarin進行網頁形式的本地APP開發,感受有點不爽,不過爲前端開發人員提供了開發APP的入口。前端
呈現引擎支持HTML 和ASP.NET MVC3的Razor引擎!android
Razor引擎是個好同志!ios
不過,建立Hybrid應用的框架不只僅是Xamarin。大名鼎鼎的Phonegape的 Apache Cordova 貌似更爲成熟些。可是基於網頁開發的Hybird應用,總感受傻不拉幾的。呵呵git
這篇文章寫的還能夠:github
原文地址:http://www.chinaz.com/program/2015/0512/405306.shtmlsql
介紹shell
最近出現的 React Native 再次讓跨平臺移動端開發這個話題火起來了,曾經你們覺得在手機上能夠像桌面那樣經過 Web 技術來實現跨平臺開發,卻大多由於性能或功能問題而放棄,不得不針對不一樣平臺開發多個版本。數據庫
但這並無阻止人們對跨平臺開發技術的探索,畢竟誰不想下降開發成本,一次編寫就到處運行呢?除了 React Native,這幾年還出現過許多其它解決方案,本文我將會對這些方案進行技術分析,供感興趣的讀者參考。json
爲了方便討論,我將它們分爲了如下 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 三、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++ 實現非界面部分比較常見,因此這裏就不重複介紹了,除了能提高性能和共用代碼,還有人使用這種方式來隱藏一些關鍵代碼(好比密鑰),若是你不知道如何構建這樣的跨平臺項目,能夠參考 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 ClassjmethodID constructor = (*env)->GetMethodID(env, cls, "", "()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 來繪製界面,由於 EGL+OpenGL 自己就是跨平臺,因此基於它來實現會很方便,目前大多數跨平臺遊戲底層都是這麼作的。
既然能夠基於 OpenGL 來開發跨平臺遊戲,是否能用它來實現界面?固然是可行的,並且 Android 4 的界面就是基於 OpenGL 的,不過它並非只用 OpenGL 的 API,那樣是不現實的,由於 OpenGL API 最初設計並非爲了畫 2D 圖形的,因此連畫個圓形都沒有直接的方法,所以 Android 4 中是經過 Skia 將路徑轉換爲位置數組或紋理,而後再交給 OpenGL 渲染的。
然而要徹底實現一遍 Android 的 UI 架構工做量不小,如下是其中部分相關代碼的代碼量:
其中光是文字渲染就很是複雜,若是你以爲簡單,那隻能說明你沒看過這個世界有多大,或許你知道中文有編碼問題、英語有連字符(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 在功能上還有很大差距
微軟知道本身的 Windows Phone 太非主流,因此很懂事地推出了將 Objective-C 項目編譯到 Windows Phone 上運行的工具,目前這個工具的相關資料不多,鑑於 Visual Studio 支持 Clang,因此極有多是使用 Clang 的前端來編譯,所以我歸到編譯流。
而對於 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, eaxmov dword [ss:esp], ebxcall imp___jump_table__[j]org.robovm.apple.coregraphics.CGRect[allocator][clinit]mov edi, eaxmov dword [ss:esp+0x4], edimov dword [ss:esp], ebxmov dword [ss:esp+0xc], 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", // black transform: 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
前面咱們說過因爲 CSS 的過於靈活拖累了瀏覽器的性能,那是否本身創建一套 UI 機制會更靠譜呢?儘管這麼作對性能確實有好處,然而它又帶來了學習成本問題,作簡單的界面問題不大,一旦要深刻定製開發就會發現相關資料太少,因此仍是不靠譜。
Titanium 還提供了一套跨平臺的 API 來方便調用,這麼作是它的優勢更是缺點,尤爲是下面三個問題:
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: [CKStackLayoutComponent newWithView:{} 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),要作得低調。
我比較喜歡的是 React Native 中用到了 Flow,它支持定義函數參數的類型,極大提高了代碼可讀性,另外還能使用 ES6 的語法,好比 class 關鍵字等。
React Native 比傳統 Objective-C 和 UIView 的學習成本低多了,熟悉 JavaScript 的開發者應該半天內就能寫個使用標準 UI 的界面,並且用 XML+CSS 畫界面也遠比 UIView 中用 Frame 進行手工佈局更易讀(我沒用過 Storyboards,它雖然看起來直觀,但多人編輯很容易衝突),感興趣能夠抽空看看這個詳細的入門教程,親自動手試試就能體會到了,Command + R 更新代碼感受很神奇。
它目前已經有組件倉庫了,並且在 github 上都有 500 多倉庫了,其中有 sqlite、Camera 等原生組件,隨着這些第三方組件的完善,基於 React Native 開發愈來愈不須要寫原生代碼了。
不過壞消息是 React Native 的 Android 版本還要等半年,這能夠理解,由於在 Android 上問題要複雜得多,有 Dalvik/ART 攔在中間,使得交互起來很麻煩。
NativeScript 和 React Native 在側重點上有很大的不一樣,使得這兩個產品目前走向了不一樣的方向:
方向的不一樣致使這兩個產品將會有不一樣的結局,我認爲 React Native 確定會完勝 NativeScript,由於它的使用風險要小不少,你能夠隨時將部分 View 使用 React Native 來試驗,遇到問題就改回 Native 實現,風險可控,而用 NativeScript 就不行了,這致使你們在技術選型的時候不敢使用 NativeScript。
話說 Angular 團隊看到 React Native 後表示不淡定了,因而開始從新設計 Angular 2 的展示架構,將現有的 Render 層獨立出來,以便於作到像 React 那樣適應不一樣的運行環境,能夠運行在 NativeScript 上。
綜合來看,我以爲 React Native 很值得嘗試,並且風險也不高。
遊戲引擎中的腳本
遊戲引擎大多都能跨平臺,爲了提高開發效率,很多引擎還內嵌了對腳本支持,好比:
- Ejecta,它實現了 Canvas 及 Audio 的 API,能夠開發簡單的遊戲,但目前還不支持 Android
- CocoonJS,實現了 WebGL 的 API,能夠運行 Three.js 寫的遊戲
- Unreal Engine 3,可使用 UnrealScript 來開發,這個語言的語法很像 Java
- Cocos2d-js,Cocos2d-x 的 JavaScript binding,它內部使用的 JS 引擎是 SpiderMonkey
- Unity 3D,可使用 C# 或 JavaScript 開發遊戲邏輯
- Corona,使用 Lua 來開發
- ...
目前這種方式只有 Unity 3D 發展比較好,Cocos2d-JS 聽說還行,有些小遊戲在使用,Corona 感受比較非主流,雖然它也支持簡單的按鈕等界面元素,但用來寫 APP 我不看好,由於不開源因此沒研究,目前看來最大的好處彷佛是虛擬機體積小,內嵌版本官方號稱只有 1.4M,這是 Lua 引擎比較大的優點。
而剩下的 3 個都基本上掛了,Ejecta 至今還不支持 Android,CocoonJS 轉型爲相似 Crosswalk 的 WebView 方案,而 Unreal Engine 4 開始再也不支持 UnrealScript,而是轉向了使用 C++ 開發,感興趣能夠圍觀一下 Epic 創始人解釋爲何要這麼作。
固然,這些遊戲引擎都不適合用來作 APP,一方面是會遇到前面提到的界面繪製問題,另外一方面遊戲引擎的實現通常都要不斷重繪,這確定比普通 App 更耗電,很容易被用戶發現後怒刪。
Adobe AIR
儘管 Flash 放棄了移動端下的瀏覽器插件版本,但 Adobe AIR 還沒掛,對於熟悉 ActionScript 的團隊來講,這是一種挺好的跨平臺遊戲開發解決方案,國內遊戲公司以前有用,如今還有沒人用我就不知道了。
但開發 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 開發 WebKit,2008 年跳槽去了 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 統計代碼就暴露了:
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 或 j2objc,j2objc 雖然目前更穩定靠譜,但它不能像 RoboVM 那樣徹底用 Java 開發,因此你還得學 Objective-C 來寫界面,而 RoboVM 的缺點就是貌似還不太穩定,並且彷佛除了遊戲之外還沒見到比較知名的應用使用,而它這種方案註定會比 j2objc 更容易出問題,因此你得作好踩坑的心理準備。
若是你只會 C#,那惟一的選擇就是 Xamarin 了。
若是你只會 Objective-C,很杯具目前沒有比較靠譜的方案,我建議你仍是學學 Java 吧,多學一門語言沒啥壞處。
若是你只會 C++,能夠作作遊戲或非 UI 的公共部分,我不建議使用 QT 或本身畫界面,仍是學學 Native 開發吧。
若是你只會 Go,還別期望用它開發移動端,由於目前的實現很低效,並且這和 Go 底層的實現機制密切相關,致使很難優化,因此預計很長一段時間內也不會有改觀。
若是你會 Rust,說明你很喜歡折騰,多半也會前面全部語言,本身作決定吧。。。