作者:iOS開(kāi)發(fā)工程師 伊澤瑞爾
目前酌毡,移動(dòng)開(kāi)發(fā)技術(shù)主要分為原生開(kāi)發(fā)和跨平臺(tái)開(kāi)發(fā)兩種肥卡。其中霜医,原生應(yīng)用是指在某個(gè)特定的移動(dòng)平臺(tái)上,使用平臺(tái)所支持的開(kāi)發(fā)工具和語(yǔ)言或南,直接調(diào)用系統(tǒng)提供的API所開(kāi)發(fā)的應(yīng)用孩等。
01
背景
原生開(kāi)發(fā)的主要優(yōu)勢(shì)體現(xiàn)在:
1.可以快速訪問(wèn)本平臺(tái)的全部功能,比如攝像頭采够、GPS等肄方;
2.原生應(yīng)用的速度快、性能高蹬癌,而且可以實(shí)現(xiàn)比較復(fù)雜的動(dòng)畫(huà)和繪制效果权她,用戶體驗(yàn)較好。
原生開(kāi)發(fā)的缺點(diǎn)也很明顯逝薪,主要體現(xiàn)在:
1.開(kāi)發(fā)成本較高隅要,不同的平臺(tái)必須維護(hù)不同的代碼,人力成本也會(huì)隨之增加董济;
2.有新的功能需要更新時(shí)步清,只能進(jìn)行版本升級(jí)。
隨著移動(dòng)互聯(lián)網(wǎng)的高速發(fā)展虏肾,在很多的業(yè)務(wù)場(chǎng)景下廓啊,傳統(tǒng)的純?cè)_(kāi)發(fā)已經(jīng)不能滿足日益增長(zhǎng)的業(yè)務(wù)需求,主要表現(xiàn)在以下兩個(gè)方面:
1.應(yīng)用動(dòng)態(tài)化的需求增大询微。當(dāng)需求發(fā)生變化崖瞭,或者是需要增加新的功能時(shí),傳統(tǒng)的純?cè)鷳?yīng)用開(kāi)發(fā)只能通過(guò)版本的升級(jí)來(lái)更新內(nèi)容撑毛,然而應(yīng)用的上架和審核都需要一定的時(shí)間书聚。因此,開(kāi)發(fā)人員迫切地希望進(jìn)行應(yīng)用內(nèi)容的更新時(shí)藻雌,可以不更新版本雌续,提升工作效率。
2.業(yè)務(wù)需求變化快胯杭,開(kāi)發(fā)成本變高驯杜。原生開(kāi)發(fā)一般需要技術(shù)團(tuán)隊(duì)對(duì)iOS、Android兩個(gè)開(kāi)發(fā)平臺(tái)進(jìn)行維護(hù)做个。當(dāng)版本更新迭代時(shí)鸽心,開(kāi)發(fā)和測(cè)試的成本都會(huì)增加滚局。
針對(duì)上述兩個(gè)問(wèn)題,跨平臺(tái)框架應(yīng)運(yùn)而生顽频。
02
跨平臺(tái)技術(shù)簡(jiǎn)介
針對(duì)上文提到的原生開(kāi)發(fā)所面臨的問(wèn)題藤肢,目前在IT界已經(jīng)誕生了很多跨平臺(tái)框架,主要分為三類:
1.H5+原生(Cordova糯景、Ionic嘁圈、微信小程序);
2.JavaScript開(kāi)發(fā)+原生渲染(React Native蟀淮、Weex最住、快應(yīng)用);
3.自繪UI+原生(Flutter)怠惶。
在本文中涨缚,我們將對(duì)React Native鳄梅、Weex和Flutter進(jìn)行對(duì)比彬犯。
1.React Native
React Native是Facebook于2015年4月開(kāi)源的跨平臺(tái)移動(dòng)應(yīng)用開(kāi)發(fā)框架,是Facebook開(kāi)源的JS框架React在原生移動(dòng)應(yīng)用平臺(tái)的衍生物擎析。React Native使用了react的設(shè)計(jì)模式览妖,但是其UI渲染轧拄、動(dòng)畫(huà)效果、網(wǎng)絡(luò)請(qǐng)求等均是由原生來(lái)實(shí)現(xiàn)的讽膏。開(kāi)發(fā)者編寫(xiě)JS代碼檩电,通過(guò)React Native的中間層轉(zhuǎn)化為原生控件,并進(jìn)行操作府树。也就是說(shuō)通過(guò)JS代碼來(lái)調(diào)用原生的組件俐末,從而實(shí)現(xiàn)相應(yīng)的功能。
React Native實(shí)現(xiàn)跨平臺(tái)的功能奄侠,主要由Java卓箫、C++和Javascript三層所構(gòu)成的。其中垄潮,C++實(shí)現(xiàn)的動(dòng)態(tài)鏈接庫(kù)(.so)烹卒,作為中間適配層橋接,實(shí)現(xiàn)了JS端與原生端的雙向通信交互弯洗。React Native會(huì)把應(yīng)用的JS代碼編譯成一個(gè)JS文件旅急,React Native整體框架目標(biāo)就是為了解釋并運(yùn)行這個(gè)JS腳本文件,如果是JS擴(kuò)展的API牡整,則直接通過(guò)bridge調(diào)用native藐吮;如果是UI界面,則映射到virtual DOM這個(gè)虛擬的JS數(shù)據(jù)結(jié)構(gòu)中,通過(guò)bridge傳遞到native谣辞,然后根據(jù)數(shù)據(jù)設(shè)置各個(gè)對(duì)應(yīng)的真實(shí)native的View迫摔。
2.Weex
在Weex設(shè)計(jì)之初,開(kāi)發(fā)者就考慮到泥从,使其能夠在三端(iOS攒菠、安卓和H5)上均能得到展現(xiàn)。在最上面的DSL歉闰,阿里一般稱之為Weex文件(.we),通過(guò)Transform轉(zhuǎn)換為js-bundle卓起,再部署到服務(wù)器和敬,這樣服務(wù)端就完成了。在客戶端戏阅,第一層是JS-Framework昼弟,最后是RenderRengine。
如上圖所示奕筐,Weex的輸入是Virtual DOM舱痘,輸出是native或H5 view,還原為內(nèi)存中的樹(shù)型數(shù)據(jù)結(jié)構(gòu)离赫,再創(chuàng)建view芭逝,把事件綁定在view上,設(shè)置view的基本屬性渊胸。Weex Render會(huì)分三個(gè)線程旬盯,不同的線程負(fù)責(zé)不同的事情,讓JS線程優(yōu)先保障流暢性翎猛。
表面上胖翰,Weex是一種客戶端技術(shù),但實(shí)際上切厘,它串聯(lián)起了從本地開(kāi)發(fā)萨咳、云端部署到分發(fā)的整個(gè)鏈路。開(kāi)發(fā)者可以在本地像編寫(xiě)Web頁(yè)面一樣先編寫(xiě)一個(gè)APP界面疫稿,然后通過(guò)命令行工具將之編譯為一段JavaScript代碼培他,生成一個(gè)Weex的JS bundle。與此同時(shí)遗座,開(kāi)發(fā)者可以將生成的JS bundle部署至云端靶壮,之后通過(guò)網(wǎng)絡(luò)請(qǐng)求或者預(yù)下發(fā)的方式加載至用戶的移動(dòng)應(yīng)用客戶端。
在移動(dòng)應(yīng)用客戶端员萍,Weex SDK會(huì)準(zhǔn)備一個(gè)JavaScript執(zhí)行環(huán)境腾降,在用戶打開(kāi)一個(gè)Weex頁(yè)面時(shí),在該環(huán)境中執(zhí)行相應(yīng)的JS bundle碎绎,并將執(zhí)行過(guò)程中產(chǎn)生的各種命令發(fā)送到native端螃壤,進(jìn)行界面渲染抗果、數(shù)據(jù)存儲(chǔ)、網(wǎng)絡(luò)通信奸晴、調(diào)用設(shè)備及用戶交互響應(yīng)等冤馏。如果用戶希望使用瀏覽器訪問(wèn)這個(gè)界面,那么他可以在瀏覽器中打開(kāi)一個(gè)相同的Web頁(yè)面寄啼,這個(gè)頁(yè)面和移動(dòng)應(yīng)用使用相同的頁(yè)面源代碼逮光,但被編譯成適合Web展示的JS Bundle,通過(guò)瀏覽器里的javaScript引擎及Weex SDK運(yùn)行起來(lái)的墩划。
3.Flutter
Flutter 是Google推出并開(kāi)源的移動(dòng)應(yīng)用開(kāi)發(fā)框架涕刚,主打跨平臺(tái)、高保真乙帮、高性能杜漠。開(kāi)發(fā)者可以通過(guò)Dart語(yǔ)言進(jìn)行APP開(kāi)發(fā),只需要一套代碼就可以同時(shí)構(gòu)建Android和iOS應(yīng)用察净,并且可以達(dá)到與原生應(yīng)用一樣的性能驾茴。Flutter還提供了豐富的組件、接口氢卡,開(kāi)發(fā)者可以高效地為 Flutter添加native擴(kuò)展锈至。此外,F(xiàn)lutter還使用了Native引擎渲染視圖译秦,為用戶提供了良好的體驗(yàn)裹赴。
Flutter與用于構(gòu)建移動(dòng)應(yīng)用程序的其它多數(shù)框架不同,因?yàn)镕lutter既不使用WebView诀浪,也不使用操作系統(tǒng)的原生控件棋返。相反,F(xiàn)lutter使用自己的高性能渲染引擎來(lái)繪制widget雷猪。這樣不僅可以保證在Android和iOS的UI一致性睛竣,而且也可以避免對(duì)原生控件依賴而帶來(lái)的限制和高昂的維護(hù)成本。
同時(shí)求摇,F(xiàn)lutter使用Skia作為2D引擎渲染射沟,Skia是Google的一個(gè)2D圖形處理函數(shù)庫(kù),在字型与境、坐標(biāo)轉(zhuǎn)換以及點(diǎn)陣圖等方面都有高效而且簡(jiǎn)潔的表現(xiàn)验夯。Skia是跨平臺(tái)的,并提供了非常友好的API摔刁。由于Android系統(tǒng)已經(jīng)內(nèi)置了Skia挥转,所以Flutter在打包APK時(shí),不需要再將Skia打包到APK中,但是iOS系統(tǒng)并未內(nèi)置Skia绑谣,所以在構(gòu)建API時(shí)党窜,必須將Skia一起打包。
03
高性能的Flutter
目前借宵,F(xiàn)lutter程序主要有兩種運(yùn)行方式:靜態(tài)編譯與動(dòng)態(tài)解釋幌衣。靜態(tài)編譯的程序在執(zhí)行前,會(huì)被全部翻譯為機(jī)器碼壤玫,通常將這種類型稱為AOT豁护,即 “提前編譯”。解釋執(zhí)行則是一句句地邊翻譯邊運(yùn)行欲间,通常將這種類型稱為JIT楚里,即“即時(shí)編譯”。
AOT程序的典型代表是用C/C++開(kāi)發(fā)的應(yīng)用括改,它們必須在執(zhí)行前編譯成機(jī)器碼。而JIT的代表則非常多家坎,如JavaScript嘱能、python等。事實(shí)上虱疏,所有腳本語(yǔ)言都支持JIT模式惹骂。但需要注意的是,JIT和AOT指的是程序運(yùn)行方式做瞪,和編程語(yǔ)言并非是強(qiáng)關(guān)聯(lián)的对粪,有些語(yǔ)言既可以以JIT方式運(yùn)行,也可以以AOT方式運(yùn)行装蓬,如Java著拭、Python,它們可以在第一次執(zhí)行時(shí)編譯成中間字節(jié)碼牍帚,然后在之后的執(zhí)行中儡遮,直接執(zhí)行字節(jié)碼。
Flutter的高性能主要靠?jī)牲c(diǎn)來(lái)保證暗赶,首先鄙币,F(xiàn)lutter APP采用Dart語(yǔ)言進(jìn)行開(kāi)發(fā)。當(dāng)Dart在 JIT模式下時(shí)蹂随,其運(yùn)行速度與 JavaScript基本持平十嘿。此外Dart支持 還AOT,當(dāng)Dart在 AOT模式下事岳锁,其運(yùn)行速度遠(yuǎn)超JavaScript绩衷。速度的提升對(duì)高幀率下的視圖數(shù)據(jù)計(jì)算很有幫助。
其次,F(xiàn)lutter使用自己的渲染引擎來(lái)繪制UI唇聘,布局?jǐn)?shù)據(jù)等由Dart語(yǔ)言直接控制版姑,所以在布局過(guò)程中不需要像RN那樣要在JavaScript和Native之間通信,在一些滑動(dòng)和拖動(dòng)的場(chǎng)景下具有明顯優(yōu)勢(shì)迟郎。由于滑動(dòng)和拖動(dòng)往往會(huì)引起布局的變化剥险,所以JavaScript需要不停地與Native之間同步布局信息,這和在瀏覽器中要JavaScript頻繁操作DOM所帶來(lái)的問(wèn)題是相同的宪肖,都會(huì)帶來(lái)比較可觀的性能開(kāi)銷表制。
04
為什么Flutter會(huì)選擇Dart語(yǔ)言?
1.開(kāi)發(fā)效率高控乾。Dart運(yùn)行時(shí)和編譯器支持Flutter的兩個(gè)關(guān)鍵特性的組合么介,分別是基于JIT的快速開(kāi)發(fā)周期和基于AOT的發(fā)布包⊥珊猓基于JIT的快速開(kāi)發(fā)周期:Flutter在開(kāi)發(fā)階段壤短,采用JIT模式,這樣就避免了每次改動(dòng)都需要進(jìn)行編譯慨仿,極大地節(jié)省了開(kāi)發(fā)時(shí)間久脯。基于AOT的發(fā)布包镰吆,F(xiàn)lutter在發(fā)布時(shí)可以通過(guò)AOT生成高效的ARM代碼帘撰,以保證應(yīng)用性能。而JavaScript則不具備這個(gè)能力万皿。
2.高性能摧找。為了實(shí)現(xiàn)流暢、高保真的的UI體驗(yàn)牢硅,F(xiàn)lutter必須在每個(gè)動(dòng)畫(huà)幀中都運(yùn)行大量的代碼蹬耘。這意味著需要一種既能支持高性能,又能保證不丟幀的周期性暫停的語(yǔ)言减余,而Dart支持AOT婆赠,在這一點(diǎn)上比JavaScript更有優(yōu)勢(shì)。
3.快速分配內(nèi)存佳励。Flutter框架使用函數(shù)式流休里,這使得它在很大程度上依賴于底層的內(nèi)存分配器。
4.類型安全赃承。由于Dart是類型安全的語(yǔ)言妙黍,支持靜態(tài)類型檢測(cè),所以可以在編譯前就發(fā)現(xiàn)一些類型的錯(cuò)誤瞧剖,并排除潛在問(wèn)題拭嫁。這對(duì)于前端開(kāi)發(fā)者來(lái)說(shuō)更具有吸引力可免。而JavaScript是一個(gè)弱類型語(yǔ)言,這也是為什么在諸多前端社區(qū)中做粤,會(huì)有眾多為JavaScript代碼添加靜態(tài)類型檢測(cè)的擴(kuò)展語(yǔ)言和工具浇借。
05
Flutter框架結(jié)構(gòu)
Flutter Framework是一個(gè)完全由Dart語(yǔ)言構(gòu)建的SDK,它實(shí)現(xiàn)了一整套自底而上的基礎(chǔ)庫(kù)怕品。
1.底部?jī)蓪?Foundation和Animation妇垢、Painting、Gestures)是Flutter引擎暴露的底層UI庫(kù)肉康,提供動(dòng)畫(huà)闯估、手勢(shì)及繪制能力。
2.Rendering層是一個(gè)抽象的布局層吼和,它依賴于dart UI層涨薪。Rendering層會(huì)構(gòu)建一個(gè)UI樹(shù),當(dāng)UI樹(shù)有變化時(shí)炫乓,它會(huì)隨即計(jì)算出有變化的部分刚夺,然后更新UI樹(shù),最終將UI樹(shù)繪制到屏幕上末捣。這個(gè)過(guò)程類似于React中的虛擬DOM侠姑。Rendering層可以說(shuō)是Flutter UI框架最核心的部分,它除了確定每個(gè)UI元素的位置塔粒、大小之外结借,還要進(jìn)行坐標(biāo)變換和繪制(調(diào)用底層dart:ui)筐摘。
3.Widgets層是Flutter提供的一套基礎(chǔ)組件庫(kù)卒茬,在基礎(chǔ)組件庫(kù)之上,F(xiàn)lutter還提供了 Material 和Cupertino兩種視覺(jué)風(fēng)格的組件庫(kù)咖熟。
Flutter Engine:這是一個(gè)完全由 C++實(shí)現(xiàn)的 SDK圃酵,其中包括了 Skia引擎、Dart運(yùn)行時(shí)和文字排版引擎等馍管。在代碼調(diào)用 dart:ui庫(kù)時(shí)郭赐,調(diào)用最終會(huì)走到Engine層,然后實(shí)現(xiàn)真正的繪制邏輯确沸。
React Native捌锭、Weex和Flutter進(jìn)行對(duì)比結(jié)果如下所示:
06
總結(jié)
從Flutter的設(shè)計(jì)理念來(lái)看,其整體架構(gòu)都是具有革命性的罗捎,相比于其他架構(gòu)观谦,它實(shí)現(xiàn)了真正意義上的跨平臺(tái)。它能夠讓各平臺(tái)的體驗(yàn)一致桨菜,并且讓用戶體驗(yàn)達(dá)到更優(yōu)』碜矗現(xiàn)如今捉偏,F(xiàn)lutter的各種UI庫(kù)和組件都在不斷增加,與之相關(guān)的各種生態(tài)系統(tǒng)和社區(qū)也在不斷完善泻红,它對(duì)新的操作系統(tǒng)的適配性將會(huì)越來(lái)越強(qiáng)夭禽。相信在不久的將來(lái),F(xiàn)lutter會(huì)慢慢成熟起來(lái)谊路,成為主流的開(kāi)發(fā)語(yǔ)言之一讹躯。