之前你們覺得在手機上可以像桌面那樣經過 Web 技術來實現跨平臺開發,卻因爲性能或其餘問題而放棄。不得不針對不一樣平臺開發多個版本號。php
這也違背了跨平臺開發的初衷。而React Native讓跨平臺移動端開發在次回到人們的視野中,其成功的緣由除了他「一次編寫到處執行」,還因爲它相比h5等前端技術,有了更接近原生的體驗。
爲了方便理解。筆者將跨平臺技術分爲4大流派:css
Web 流,如你們熟知的PhoneGap/Cordova等技術。它將原生的接口封裝後暴露給 JavaScript。而後經過系統自帶的 WebView 執行。也可以視野本身內嵌Chrome內核。
Web 流最常被吐槽的就是性能差,渲染速度慢。現在說到 Web 性能差主要說的是 Android 下比較差,在 iOS 下已經很是流暢了。
說到性能差,主要緣由是在Android和ios的早期設備中。因爲沒有實現GPU加速,因此形成每次重繪界面的卡頓。
而形成渲染慢的第二個緣由是:css過於複雜。因爲從實現原理上看 Chrome 和 Android View 並無本質上的差異,但過於複雜的css會加劇gpu的負擔。前端
那是否是可以經過簡化 CSS 來解決?實際上還真有人這麼嘗試了,比方 Famo.us,它最大的特點就是不讓你寫 CSS,僅僅能使用固定的幾種佈局方法,全然靠 JavaScript 來寫界面,因此它能有效避免寫出低效的 CSS,從而提高性能。
形成繪製緩慢的第三個緣由是,業務需求的複雜。如要實現超長的 ListView商品展現。java
因爲 DOM 是一個很是上層的 API,使得 JavaScript 沒法作到像 Native 那樣細粒度的控制內存及線程,因此難以進行優化。則在硬件較差的機器上會比較明顯
上面三個問題現在都很差解決。事實上除了性能以外,Web 流更嚴重的問題是功能缺失。react
比方 iOS 8 就新增 4000+ API。而 Web 標準需要漫長的編寫和評審過程。而等到web審覈經過,即使是Cordova這樣的框架本身封裝也是忙只是來的。因此爲了更好地使用系統新功能,Native是最快的選擇。android
儘管前面提到 HTML/CSS 過於複雜致使性能問題,但事實上這正是 Web 最大的優點所在,因爲 Web 最初的目的就是顯示文檔,假設你想顯示豐富的圖文排版。儘管 iOS/Android 都有富文本組件。但比起 CSS 差太遠了,因此在很是多 Native 應用中是不可避免要嵌 Web 的。ios
不一樣平臺下的官方語言不同,而且平臺對官方語言的支持最好,這就致使相同的邏輯,咱們需要寫多套代碼。比方Android平臺用Java。ios用oc或者swift。因而就有人想到了經過代碼轉換的方式來下降反覆的工做量,這就是因此的代碼轉換流。
這樣的方式儘管聽起來不是很是靠譜。但它倒是成本和風險都最小的。因爲代碼轉換後就可以用官方提供的各類工具了。和普通開發差異不大。而且轉換後。可以利用原生的優勢,這也可以下降兼容的問題。
眼下存在的幾種代碼轉換方式:程序員
j2objc 能將 Java 代碼轉成 Objective-C。聽說 Google 內部就是使用它來下降跨平臺開發成本的。比方 Google Inbox 項目就號稱經過它共用了 70% 的代碼,效果很是顯著。
可能有人會認爲奇怪,爲什麼 Google 要專門開發一個幫助你們寫 Objective-C 的工具?還有媒體說 Google 作了件好事。事實上吧,我認爲 Google 這算盤打得不錯。因爲基本上重要的應用都會同一時候開發 Android 和 iOS 版本號,有了這個工具就意味着。你可以先開發 Android 版本號,而後再開發 iOS 版本號。web
。。objective-c
既然都有成功案例了,這個方法確實值得嘗試,而且關鍵是會 Java 的人多啊,可以經過它來高速移植代碼到 Objective-C 中。
除了有 Java 轉成 Objective-C,還有 Objective-C 轉成 Java 的方案,那就是 MyAppConverter,比起前面的 j2objc,這個工具更有野心。它還打算將 UI 部分也包括進來。從它已轉換的列表中可以看到還有 UIKit、CoreGraphics 等組件,使得有些應用可以不改代碼就能轉成功,只是這點我並不看好,對於大部分應用來講並不現實。
因爲眼下是收費項目,我沒有嘗試過,對技術細節也不瞭解。因此這裏不作評價。
Mono 提供了一個將 Java 代碼轉成 C# 的工具 Sharpen。只是彷佛用的人很少,Star 才 118,因此看起來不靠譜。
還有 JUniversal 這個工具可以將 Java 轉成 C#,但眼下它並無公佈公開版本號。因此詳細狀況還待了解,它的一個特點是自帶了簡單的跨平臺庫,裏面包括文件處理、JSON、HTTP、OAuth 組件。可以基於它來開發可複用的業務邏輯。
比起轉成 Objective-C 和 Java 的工具,轉成 C# 的這兩個工具看起來都很是不成熟。預計是用 Windows Phone 的人少。
除了前面提到的源代碼到源代碼的轉換,還有 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 耦合,因此公共部分很少。其借鑑性意義不大。
編譯流比代碼轉換流的代碼轉換更進一步,它直接將某個語言編譯爲普通平臺下可以識別的二進制文件。採用這樣的方式主要有如下特色:
優勢
缺點
接下來,咱們經過不一樣的語言來介紹這個流派下不一樣的技術實現。
因爲眼下Android、iOS和Windows Phone都提供了對C++開發的支持。特別是C++ 在實現非界面部分,性能是很是高效的。
而假設C++ 要實現非界面部分。仍是比較有挑戰的。這主要是因爲Android 的界面絕大部分是 Java 實現。而在 iOS 和 Windows Phone下可以分別使用C++的超集Objective-C++和 C++/CX來開發。
那麼要解決用C++開發Android界面,眼下主要有兩種方案:
第一種方式儘管可行,但是代碼冗餘高。實現過於複雜。那另一種方式呢,比方 JUCE 和 QT就是本身用代碼畫的。
只是在Android 5下就悲劇了。很是多效果都沒出來,比方按鈕沒有漣漪效果。根本緣由在於它是經過 Qt Quick Controls 的本身定義樣式來模擬的。而不是使用系統UI組件。所以它享受不到系統升級本身主動帶來的界面優化。
固然咱們可以使用OpenGL來繪製界面,因爲EGL+OpenGL自己就是跨平臺的。而且眼下大多數跨平臺遊戲底層都是這麼作的。
既然可以基於 OpenGL 來開發跨平臺遊戲,可否用它來實現界面?固然是可行的。而且Android 4的界面就是基於OpenGL的。只是它並不是僅僅用 OpenGL 的 API。那樣是不現實的。因爲 OpenGL API 最初設計並不是爲了畫 2D 圖形的,因此連畫個圓形都沒有直接的方法,所以Android 4中是經過Skia將路徑轉換爲位置數組或紋理,而後再交給 OpenGL 渲染的。
然而直接使用OpenGL來作界面的繪製,代價是很是大的,而且眼下在各個平臺下都會有良好的官方支持。
所以對於大多數應用來講本身畫UI是很是不划算的。
Xamarin 可以使用 C# 來開發 Android 及 iOS 應用,它是從 Mono 發展而來的,眼下看起來商業運做得不錯,相關工具及文檔都挺健全。
因爲它在 iOS 下是以 AOT 的方式編譯爲二進制文件的。因此把它歸到編譯流來討論。事實上它在 Android 是內嵌了 Mono 虛擬機 來實現的。所以需要裝一個 17M 的執行環境。
在 UI 方面,它可以經過調用系統 API 來使用系統內置的界面組件,或者基於 Xamarin.Forms 開發定製要求不高的跨平臺 UI。
對於熟悉 C# 的團隊來講,這還真是一個看起來很是不錯的,但這樣的方案最大的問題就是相關資料不足。遇到問題很是可能搜不到解決方式,只是因爲時間關係我並無細緻研究,推薦看看這篇文章,當中談到它的優缺點是:
優勢
缺點
微軟知道本身的 Windows Phone 太非主流,因此很是懂事地推出了將 Objective-C 項目編譯到 Windows Phone 上執行的工具,眼下這個工具的相關資料很是少,鑑於 Visual Studio 支持 Clang,因此極有多是使用 Clang 的前端來編譯。這樣最大的優勢是之後支持 Swift 會很是方便。所以我歸到編譯流。
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,太非主流了,因此不值得研究它。
Go作爲後端服務開發語言,專門針對多處理器系統應用程序的編程進行了優化。使用Go編譯的程序可以媲美C或C++代碼的速度,而且更加安全、支持並行進程。
Go 從 1.4 版本號開始支持開發Android應用(並在1.5 版本號支持iOS)。
儘管能同一時候支持Android和ios。但是眼下可用的api很是少,Go仍然專一於後端語言開發。
Android的View層全然是基於Java寫的,要想用Go來寫UI不可避免要調用Java 代碼,而這方面Go尚未簡便的方式,眼下Go調用外部代碼僅僅能使用cgo。經過cgo再調用jni,這就不可避免的需要寫很是多的中間件。
而且 cgo 的實現自己就對性能有損失,除了各類無關函數的調用,它還會鎖定一個 Go 的系統線程。這會影響其餘 gorountine 的執行,假設同一時候執行太多外部調用。甚至會致使所有 gorountine 等待
因此使用Go開發跨平臺移動端應用眼下不靠譜。
除了編譯爲不一樣平臺下的二進制文件。還有還有一種常見作法是經過虛擬機來支持跨平臺執行。比方 JavaScript 和 Lua 都是天生的內嵌語言,因此在這個流派中很是多方案都使用了這兩個語言。
只是虛擬機流會遇到兩個問題:一個是性能損耗。還有一個是虛擬機自己也會佔不小的體積。
說到跨平臺虛擬機你們都會想到 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 應該很多人聽過,它和 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 */
}
前面咱們說過因爲 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一模一樣。也是將JavaScript將翻譯爲Objective-C而後由官方的方案執行。只是這項目開發都快三年了,但至今仍然是試驗階段,顯然有點不靠譜。
以前說到 Titanium 本身定義 API 帶來的各類問題,因而就有人換了個思路,比方前段時間推出的 NativeScript,它的方法說白了就是用工具來本身主動生成 wrapper API。和系統 API 保持一致。
有了這個本身主動生成 wrapper 的工具,它就能方便基於系統 API 來開發跨平臺組件。以簡單的 Button 爲例,源代碼在 cross-platform-modules/ui/button 中,它在 Android 下是這樣實現的:
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是由FaceBook開源的基於JavaScript和React搭建的一套跨平臺開發框架。而在設計之初,React Native採用就是在不一樣平臺下使用平臺自帶的UI組件。
覺得它採用JavaScript和React來開發,因此得到了很多前端程序員的青睞。
有人說。React Native採用js等前端技術是迴歸H5,但事實上 React Native和Web 扯不上太多關係。ReactNative儘管借鑑了CSS中的Flexbox、navigator、XMLHttpRequest 等Api的寫法,但是大部分仍是經過原生的組件或者本身封裝的組件來開發的。就像FaceBooK的內部軟件Facebook Groups。iOS版本號很是大一部分基於React Native開發。當中用到了很多內部通用組件。
React Native相比傳統Objective-C和UIView,學習成本更低了。熟悉JavaScript 的開發人員可以在半天寫個使用標準UI界面,而且用XML+CSS 畫界面也遠比 UIView 中用 Frame 進行手工佈局更易讀。
在加上React Native師出名門,截止眼下,React Native已更新到0.4.2版本號,而且逐步趨於穩定。
因爲其更加接近原生的體驗。國內一些大廠紛紛增長,諸如阿里、騰訊、美團等紛紛開始本身app想React Native的改造。
React Native代碼:
@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]},
}];
}
...
很是多人認爲跨平臺歷來都不靠譜。但事實上是有的,那就是 Web,這個歷史上最成功的樣例。就像當年web擠掉pc成爲互聯網的主流同樣,將來的互聯網必將是web的時代。