作者:個推iOS工程師 伊澤瑞爾
一、背景
目前,移動開發技術主要分為原生開發和跨平臺開發兩種。其中,原生應用是指在某個特定的移動平臺上,使用平臺所支援的開發工具和語言,直接呼叫系統提供的API所開發的應用。 原生開發的主要優勢體現在: 1.可以快速訪問本平臺的全部功能,比如攝像頭、GPS等; 2.原生應用的速度快、效能高,而且可以實現比較複雜的動畫和繪製效果,使用者體驗較好。 原生開發的缺點也很明顯,主要體現在: 1.開發成本較高,不同的平臺必須維護不同的程式碼,人力成本也會隨之增加; 2.有新的功能需要更新時,只能進行版本升級。 隨著移動網際網路的高速發展,在很多的業務場景下,傳統的純原生開發已經不能滿足日益增長的業務需求,主要表現在以下兩個方面: 1.應用動態化的需求增大。當需求發生變化,或者是需要增加新的功能時,傳統的純原生應用開發只能通過版本的升級來更新內容,然而應用的上架和稽核都需要一定的時間。因此,開發人員迫切地希望進行應用內容的更新時,可以不更新版本,提升工作效率。 2.業務需求變化快,開發成本變高。原生開發一般需要技術團隊對iOS、Android兩個開發平臺進行維護。當版本更新迭代時,開發和測試的成本都會增加。 針對上述兩個問題,跨平臺框架應運而生。
二、跨平臺技術簡介
針對上文提到的原生開發所面臨的問題,目前在IT界已經誕生了很多跨平臺框架,主要分為三類: 1.H5+原生(Cordova、Ionic、微信小程式); 2.JavaScript開發+原生渲染(React Native、Weex、快應用); 3.自繪UI+原生(Flutter)。 在本文中,我們將對React Native、Weex和Flutter進行對比。
1.React Native
React Native是Facebook於2015年4月開源的跨平臺移動應用開發框架,是Facebook開源的JS框架React在原生移動應用平臺的衍生物。React Native使用了react的設計模式,但是其UI渲染、動畫效果、網路請求等均是由原生來實現的。開發者編寫JS程式碼,通過React Native的中間層轉化為原生控制元件,並進行操作。也就是說通過JS程式碼來呼叫原生的元件,從而實現相應的功能。 React Native實現跨平臺的功能,主要由Java、C++和Javascript三層所構成的。其中,C++實現的動態連結庫(.so),作為中間適配層橋接,實現了JS端與原生端的雙向通訊互動。React Native會把應用的JS程式碼編譯成一個JS檔案,React Native整體框架目標就是為了解釋並執行這個JS指令碼檔案,如果是JS擴充套件的API,則直接通過bridge呼叫native;如果是UI介面,則對映到virtual DOM這個虛擬的JS資料結構中,通過bridge傳遞到native,然後根據資料設定各個對應的真實native的View。
2.Weex
在Weex設計之初,開發者就考慮到,使其能夠在三端(iOS、安卓和H5)上均能得到展現。在最上面的DSL,阿里一般稱之為Weex檔案(.we),通過Transform轉換為js-bundle,再部署到伺服器,這樣服務端就完成了。在客戶端,第一層是JS-Framework,最後是RenderRengine。
如上圖所示,Weex的輸入是Virtual DOM,輸出是native或H5 view,還原為記憶體中的樹型資料結構,再建立view,把事件繫結在view上,設定view的基本屬性。Weex Render會分三個執行緒,不同的執行緒負責不同的事情,讓JS執行緒優先保障流暢性。表面上,Weex是一種客戶端技術,但實際上,它串聯起了從本地開發、雲端部署到分發的整個鏈路。開發者可以在本地像編寫Web頁面一樣先編寫一個APP介面,然後通過命令列工具將之編譯為一段JavaScript程式碼,生成一個Weex的JS bundle。與此同時,開發者可以將生成的JS bundle部署至雲端,之後通過網路請求或者預下發的方式載入至使用者的移動應用客戶端。 在移動應用客戶端,Weex SDK會準備一個JavaScript執行環境,在使用者開啟一個Weex頁面時,在該環境中執行相應的JS bundle,並將執行過程中產生的各種命令傳送到native端,進行介面渲染、資料儲存、網路通訊、呼叫裝置及使用者互動響應等。如果使用者希望使用瀏覽器訪問這個介面,那麼他可以在瀏覽器中開啟一個相同的Web頁面,這個頁面和移動應用使用相同的頁面原始碼,但被編譯成適合Web展示的JS Bundle,通過瀏覽器裡的javaScript引擎及Weex SDK執行起來的。
3.Flutter
Flutter 是Google推出並開源的移動應用開發框架,主打跨平臺、高保真、高效能。開發者可以通過Dart語言進行APP開發,只需要一套程式碼就可以同時構建Android和iOS應用,並且可以達到與原生應用一樣的效能。Flutter還提供了豐富的元件、介面,開發者可以高效地為 Flutter新增native擴充套件。此外,Flutter還使用了Native引擎渲染檢視,為使用者提供了良好的體驗。 Flutter與用於構建移動應用程式的其它多數框架不同,因為Flutter既不使用WebView,也不使用作業系統的原生控制元件。相反,Flutter使用自己的高效能渲染引擎來繪製widget。這樣不僅可以保證在Android和iOS的UI一致性,而且也可以避免對原生控制元件依賴而帶來的限制和高昂的維護成本。 同時,Flutter使用Skia作為2D引擎渲染,Skia是Google的一個2D圖形處理函式庫,在字型、座標轉換以及點陣圖等方面都有高效而且簡潔的表現。Skia是跨平臺的,並提供了非常友好的API。由於Android系統已經內建了Skia,所以Flutter在打包APK時,不需要再將Skia打包到APK中,但是iOS系統並未內建Skia,所以在構建API時,必須將Skia一起打包。
三、高效能的Flutter
目前,Flutter程式主要有兩種執行方式:靜態編譯與動態解釋。靜態編譯的程式在執行前,會被全部翻譯為機器碼,通常將這種型別稱為AOT,即 “提前編譯”。解釋執行則是一句句地邊翻譯邊執行,通常將這種型別稱為JIT,即“即時編譯”。 AOT程式的典型代表是用C/C++開發的應用,它們必須在執行前編譯成機器碼。而JIT的代表則非常多,如JavaScript、python等。事實上,所有指令碼語言都支援JIT模式。但需要注意的是,JIT和AOT指的是程式執行方式,和程式語言並非是強關聯的,有些語言既可以以JIT方式執行,也可以以AOT方式執行,如Java、Python,它們可以在第一次執行時編譯成中間位元組碼,然後在之後的執行中,直接執行位元組碼。 Flutter的高效能主要靠兩點來保證,首先,Flutter APP採用Dart語言進行開發。當Dart在 JIT模式下時,其執行速度與 JavaScript基本持平。此外Dart支援 還AOT,當Dart在 AOT模式下事,其執行速度遠超JavaScript。速度的提升對高幀率下的檢視資料計算很有幫助。 其次,Flutter使用自己的渲染引擎來繪製UI,佈局資料等由Dart語言直接控制,所以在佈局過程中不需要像RN那樣要在JavaScript和Native之間通訊,在一些滑動和拖動的場景下具有明顯優勢。由於滑動和拖動往往會引起佈局的變化,所以JavaScript需要不停地與Native之間同步佈局資訊,這和在瀏覽器中要JavaScript頻繁操作DOM所帶來的問題是相同的,都會帶來比較可觀的效能開銷。
四、為什麼Flutter會選擇Dart語言?
1.開發效率高。
Dart執行時和編譯器支援Flutter的兩個關鍵特性的組合,分別是基於JIT的快速開發週期和基於AOT的釋出包。基於JIT的快速開發週期:Flutter在開發階段,採用JIT模式,這樣就避免了每次改動都需要進行編譯,極大地節省了開發時間。基於AOT的釋出包,Flutter在釋出時可以通過AOT生成高效的ARM程式碼,以保證應用效能。而JavaScript則不具備這個能力。 2.高效能。
為了實現流暢、高保真的的UI體驗,Flutter必須在每個動畫幀中都執行大量的程式碼。這意味著需要一種既能支援高效能,又能保證不丟幀的週期性暫停的語言,而Dart支援AOT,在這一點上比JavaScript更有優勢。 3.快速分配記憶體。
Flutter框架使用函式式流,這使得它在很大程度上依賴於底層的記憶體分配器。
4.型別安全。
由於Dart是型別安全的語言,支援靜態型別檢測,所以可以在編譯前就發現一些型別的錯誤,並排除潛在問題。這對於前端開發者來說更具有吸引力。而JavaScript是一個弱型別語言,這也是為什麼在諸多前端社群中,會有眾多為JavaScript程式碼新增靜態型別檢測的擴充套件語言和工具。
五、Flutter框架結構
Flutter Framework是一個完全由Dart語言構建的SDK,它實現了一整套自底而上的基礎庫。 1.底部兩層(Foundation和Animation、Painting、Gestures)是Flutter引擎暴露的底層UI庫,提供動畫、手勢及繪製能力。2.Rendering層是一個抽象的佈局層,它依賴於dart UI層。Rendering層會構建一個UI樹,當UI樹有變化時,它會隨即計算出有變化的部分,然後更新UI樹,最終將UI樹繪製到螢幕上。這個過程類似於React中的虛擬DOM。Rendering層可以說是Flutter UI框架最核心的部分,它除了確定每個UI元素的位置、大小之外,還要進行座標變換和繪製(呼叫底層dart:ui)。
3.Widgets層是Flutter提供的一套基礎元件庫,在基礎元件庫之上,Flutter還提供了 Material 和Cupertino兩種視覺風格的元件庫。
Flutter Engine:這是一個完全由 C++實現的 SDK,其中包括了 Skia引擎、Dart執行時和文字排版引擎等。在程式碼呼叫 dart:ui庫時,呼叫最終會走到Engine層,然後實現真正的繪製邏輯。 React Native、Weex和Flutter進行對比結果如下所示:
六、總結
從Flutter的設計理念來看,其整體架構都是具有革命性的,相比於其他架構,它實現了真正意義上的跨平臺。它能夠讓各平臺的體驗一致,並且讓使用者體驗達到更優。現如今,Flutter的各種UI庫和元件都在不斷增加,與之相關的各種生態系統和社群也在不斷完善,它對新的作業系統的適配性將會越來越強。相信在不久的將來,Flutter會慢慢成熟起來,成為主流的開發語言之一。