原生開發技術
什麼是原生開發?
原生開發指的是純原生應用程式(簡稱App)開發,是在Android、iOS等移動平臺上利用官方提供的開發語言、開發類庫以及開發工具等進行App開發。比如Android App就是指使用Java或Kotlin開發語言在Eclipse或Android Studio的開發工具上直接呼叫Android SDK API開發的App;而iOS App就是指通過Objective-C或Swift開發語言在Xcode的開發工具上直接呼叫iOS SDK API開發的App。
原生開發的優點和缺點有哪些?
原生App開發代表著較好的使用者體驗和更快更高的效能,但是原生App的可移植性比較差,特別是一款原生App,Android和iOS都要各自開發,同樣的邏輯、介面都要寫兩套。
主要優點:
- 可訪問Android/iOS(平臺) 提供的全部功能(如GPS、攝像頭...);
- 速度快、效能高以及可實現複雜動畫及繪製和整體使用者體驗好。
主要缺點:
- 平臺特定、開發成本高;不同平臺必須維護不同程式碼,人力成本隨之變大;
- 內容固定,動態化弱;大多數情況下,有新功能更新、Bug修復完時只能重新發版,使用者需將App重新下載升級。
跨平臺開發技術
背景
在移動網際網路發展初期,業務場景並不複雜,原生開發還可以應對產品需求迭代。但近幾年,隨著物聯網時代到來,移動網際網路高歌猛進、日新月異,在很多業務場景中,傳統的純原生開發已經不能滿足日益增長的業務需求。
主要表現在:
- 動態化內容需求增大,當需求發生變化時,純原生應用需要通過版本升級來更新內容,但應用上架、稽核是需要時間週期的,這對高速變化的網際網路時代來說是很難接受的,因此對應用動態化(不發版也可以更新應用內容)的需求就變的迫在眉睫。
- 業務需求變化快,開發成本變大,由於原生開發的程式碼一般都要Android、iOS兩個開發團隊進行維護,而且版本迭代時,無論人力成本還是測試成本都會變大。
因此針對純原生開發主要面臨的動態化和開發成本這兩個問題,誕生了一些跨平臺的動態化框架。
H5+原生混合開發
這類框架主要原理是將App的一部分需要動態變動的內容通過H5來實現,使用原生平臺的網頁載入控制元件WebView(Android)或WKWebView(iOS)來載入。這樣一來,H5部分是可以隨時改變而不用發版的,而且能滿足動態化需求,同時,由於H5程式碼只需要一次開發,就能在Android和iOS兩個平臺上執行,這也可以減少開發成本,也就是說H5部分功能越多,開發成本就越小,我們稱這種H5+原生的開發模式為混合(Hybrid)開發,採用混合模式開發的App稱之為混合應用或Hybrid App,如果一個App的大多數功能都是H5實現的話,我們稱其為Web App。
目前混合開發框架的典型代表有:淘寶、Cordova和Ionic以及微信小程式等,值得一提的是微信小程式目前是在WebView中渲染的,但將來有可能會採用原生渲染。
混合開發技術點:JS程式碼與原生程式碼之間的通訊
綜上所述,原生開發可以訪問平臺全部功能,但混合開發中,H5程式碼是執行在WebView中,而WebView實質上就是一個瀏覽器核心,其JavaScript依然執行在一個許可權受限的沙箱中,所以對於大多數系統的訪問能力都受到了限制,如無法訪問檔案系統、不能使用藍芽等。所以,對於H5不能實現的功能,都需要原生去做。而混合框架一般都會在原生程式碼中預先實現一些JavaScript需要訪問系統能力的API,然後暴露給WebView以供JavaScript呼叫,這樣一來,WebView就成為了JavaScript與原生API之間通訊的橋樑,主要負責JavaScript與原生之間傳遞呼叫訊息,而訊息的傳遞必須遵守一個標準的協議,它規定了訊息的格式與含義,我們把依賴於WebView的,用於在JavaScript與原生之間通訊並實現了某種訊息傳輸協議的工具稱之為WebView JavaScript Bridge(簡稱JsBridge),它也是混合開發框架的核心。
示例:JavaScript呼叫原生API獲取手機型號
下面我們以Android為例,實現一個獲取手機型號的原生API供JavaScript呼叫。在這個示例中將展示JavaScript呼叫原生API的流程,讀者可以直觀的感受一下呼叫流程。我們選用Github上開源的dsBridge(Android版、iOS版)來進行通訊。dsBridge是一個支援同步呼叫的跨平臺的JsBridge,此示例中只使用其同步呼叫功能。
1.首先在原生中實現獲取手機型號的API
class JSAPI{
@JavascriptInterface
public Object getPhoneModel(Object msg) {
return Build.MODEL;
}
}
複製程式碼
2.將原生API通過WebView註冊到JsBridge中
import wendu.dsbridge.DWebView
...
//DWebView繼承自WebView,由dsBridge提供
DWebView dwebView= (DWebView) findViewById(R.id.dwebview);
//註冊原生API到JsBridge
dwebView.addJavascriptObject(new JSAPI(), null);
複製程式碼
3.在JavaScript中呼叫原生API
var dsBridge=require("dsbridge")
//直接呼叫原生API `getPhoneModel`方法
var model=dsBridge.call("getPhoneModel");
//印表機型
console.log(model);
複製程式碼
上面示例演示了JavaScript呼叫原生API的過程,同樣的,一般來說優秀的JsBridge也支援原生呼叫JavaScript,dsBridge也是支援的,如果您感興趣,可以去Github dsBridge(Android版、iOS版)專案主頁檢視。
總結
混合應用的優點是動態內容是H5,新功能增加、Bug的修復完無需讓App再次發版,Web技術棧、社群及資源豐富,缺點就是效能不好,對於複雜使用者介面或動畫,WebView不堪重任。
JavaScript開發+原生渲染
目前市面上流行的JavaScript開發+原生渲染的跨平臺框架有快應用、Weex、React Native(簡稱RN)。
由於RN和React原理相通,並且Flutter也是受React啟發,很多思想也都是相通的,萬丈高樓平地起,我們有必要深入瞭解一下React原理,React是一個響應式的Web框架,我麼先了解兩個重要的概念:DOM樹與響應式程式設計。
DOM樹
文件物件模型(Document Object Model,簡稱DOM),是W3C組織推薦的處理可擴充套件標誌語言的標準程式設計介面,一種獨立於平臺和語言的方式訪問和修改一個文件的內容和結構。換句話說,這是表示和處理一個HTML或XML文件的標準介面。簡單來說,DOM就是文件樹,與使用者介面控制元件樹對應,在前端開發中通常指HTML對應的渲染樹,但廣義的DOM也可以指Android中的XML佈局檔案對應的控制元件樹,而術語DOM操作就是指直接來操作渲染樹(或控制元件樹),因此,可以看到其實DOM樹和控制元件樹是等價的概念,只不過前者常用於Web開發中,而後者常用於原生開發中。
響應式程式設計
React中提出一個重要思想:狀態改變則UI隨之自動改變,而React框架本身就是響應使用者狀態改變的事件而執行重新構建使用者介面的工作,這就是典型的響應式程式設計方式,下面我們總結一下React中響應式原理:
- 開發者只需關注狀態轉移(資料),當狀態發生變化,React框架會自動根據新的狀態重新構建UI。
- React框架在接收到使用者狀態改變通知後,會根據當前渲染樹,結合最新的狀態改變,通過Diff演算法,計算出樹中變化的部分,然後只更新變化的部分(DOM操作),從而避免整棵樹重構,提高效能。
值得注意的是,在上述第二步中,狀態變化後React框架並不會立即去計算並渲染DOM樹的變化部分,相反,React會在DOM的基礎上建立一個抽象層,即虛擬DOM樹,對資料和狀態所做的任何改動,都會被自動且高效的同步到虛擬DOM中,最後再批量同步到真實DOM中,而不是每次改變都去操作一下DOM。為什麼不能每次改變都直接去操作DOM樹?這是因為在瀏覽器中每一次DOM操作都有可能引起瀏覽器的重繪或迴流:
- 如果DOM只是外觀風格發生變化,如顏色變化,會導致瀏覽器重繪介面;
- 如果DOM樹的結構發生變化,如尺寸、佈局以及節點隱藏等,會導致瀏覽器迴流及重新排版佈局。
而瀏覽器的重繪和迴流都是比較昂貴的操作,如果每一次改變都直接對DOM進行操作,這會帶來效能問題,而批量操作只會觸發一次DOM更新。
JavaScript開發+原生渲染的跨平臺框架
RN
RN是Facebook於2015年4月開源的跨平臺移動應用開發框架,是Facebook早先開源的JS框架React在原生移動應用平臺的衍生產物,目前支援iOS和Android兩個平臺。RN使用JavaScript語言,類似於HTML的JSX(即JavaScript XML——一種在React組建內部構建標籤的類XML語法),以及CSS來開發移動應用,因此熟悉Web前端開發的技術人員只需很少的學習就可以進入移動應用開發領域。
RN是React在原生移動應用平臺的衍生產物,那兩者主要的區別是什麼呢?其實,主要的區別在於虛擬DOM對映的物件是什麼?React中虛擬DOM最終會對映為瀏覽器DOM樹,而RN中虛擬DOM會通過JavaScriptCore對映為原生控制元件樹。
1、JavaScriptCore是一個JavaScript直譯器,它在RN中主要有兩個作用:
①、為JavaScript提供執行環境;
②、是JavaScript與原生之間通訊的橋樑,作用和JsBridge一樣,事實上,在iOS中,很多JsBridge的實現都是基於JavaScriptCore。
2、RN中通過JavaScriptCore將虛擬DOM對映為原生控制元件的過程分為兩步:
①、佈局訊息傳遞,將虛擬DOM佈局資訊傳遞給原生;
②、原生根據佈局資訊通過對應的原生控制元件渲染控制元件樹。
至此,RN便實現了跨平臺,相對於混合應用,因為RN是原生控制元件渲染,所以效能會比混合應用中的H5好很多,同時RN是Web開發技術棧,也只需維護一份程式碼,即可在多個平臺上使用。
Weex
Weex是阿里巴巴於2016年釋出的跨平臺移動應用開發框架,思想及原理和RN類似,最大的不同是語法層面,RN只支援JSX語法,而Weex支援Vue語法和Rax語法,Rax的DSL語法是基於React JSX語法而創造的,與React不同,在Rax中JSX是必選的,它不支援通過其他方式建立元件,所以學習JSX是使用Rax的必要基礎。
快應用
快應用是華為、小米和OPPO以及魅族等國內9大主流手機廠商共同制定的輕量級應用標準,目標直指小程式。它也是採用JavaScript語言開發,原生控制元件渲染,與RN和Weex相比主要有兩點不同:
- 快應用自身不支援Vue或React語法,其採用原生JavaScript開發,開發框架和微信小程式很像,值得一提的是小程式目前已經可以使用Vue語法開發(mpvue),從原理上來講,Vue的語法也可以移植到快應用上。
- RN和Weex的渲染/排版引擎是整合到框架中的,每一個App都需要打包一份,安裝包體積較大;而快應用渲染/排版引擎是整合到ROM中的,應用中無需打包,安裝包體積小,正因如此,快應用才能在保證效能的同時做到快速分發。
總結
JavaScript開發+原生渲染的方式主要優點如下:
- 採用Web開發技術棧、社群龐大和上手快以及開發成本相對較低;
- 原生渲染,效能相比H5提高很多;
- 動態化較好,支援熱更新。
JavaScript開發+原生渲染的方式主要缺點如下:
- 渲染時需要JavaScript和原生之間通訊,在有些場景,如拖動可能會因為通訊頻繁導致卡頓;
- JavaScript為指令碼語言,執行時需要JIT(即時編譯),執行效率和AOT(提前編譯)程式碼仍有差距;
- 由於渲染依賴原生控制元件,不同平臺的控制元件需要單獨維護,並且當系統更新時,社群控制元件可能會滯後;除此之外,其控制元件系統也會受到原生UI系統限制,例如,在Android中,手勢衝突消歧規則是固定的,這在使用不同人寫的控制元件巢狀時,手勢衝突問題將會變得非常棘手。
自繪UI+原生
自繪UI+原生是一種跨平臺技術,這種技術的思路是:通過在不同平臺實現一個統一介面的渲染引擎來繪製UI,而不依賴系統原生控制元件,所以可以做到不同平臺UI的一致性。注意,自繪引擎解決的是UI跨平臺問題,如果涉及其他系統能力呼叫,依然要依賴原生開發。
自繪UI+原生的優點如下:
- 效能高;因為自繪引擎是直接呼叫系統API來繪製UI,所以效能和原生控制元件接近。
- 靈活、元件庫易維護以及UI外觀保真度和一致性高;因為UI渲染不依賴原生控制元件,也就不需要根據不同平臺的控制元件單獨維護一套元件庫,所以程式碼容易維護;由於元件庫是同一套程式碼、同一個渲染引擎,所以在不同平臺,元件顯示外觀可以做到高保真和高一致性;另外,由於不依賴原生控制元件,也就不會受原生布局系統的限制,這樣佈局系統會非常靈活。
自繪UI+原生的缺點如下:
- 動態性不足;因為為了保證UI繪製效能,自繪UI系統一般都會採用AOT(提前編譯)模式編譯其釋出包,所以應用釋出後,不能像Hybrid和RN那些使用JavaScript(JIT,即時編譯)作為開發語言的框架那樣動態下發程式碼。
也許你已經猜到Flutter就屬於這一類跨平臺技術,沒錯,Flutter實現了一套自繪引擎,並擁有一套自己的UI佈局系統。不過,自繪引擎的思路並不是什麼新概念,Flutter也並不是第一個嘗試這麼做的,在它之前就有一個典型的代表——QT。
QT
QT是一個1991年由Qt Company開發的跨平臺C++圖形使用者介面應用程式開發框架。2008年,Qt Company科技被諾基亞公司收購,Qt也因此成為諾基亞旗下的程式語言工具。2012年,Qt被Digia收購。2014年4月,跨平臺整合開發環境Qt Creator 3.1.0正式釋出,實現了對於iOS的完全支援,新增WinRT、Beautifier等外掛,廢棄了無Python介面的GDB除錯支援,整合了基於Clang的C/C++程式碼模組,並對Android支援做出了調整,至此實現了全面支援iOS、Android、WP,它提供給應用程式開發者構建圖形使用者介面所需的所有功能。但是,QT雖然在PC端獲得了巨大成功,備受社群追捧,然而其在移動端卻表現不佳,在近幾年,雖然偶爾能聽到QT的聲音,但一直很弱,無論QT本身技術如何、設計思想如何,但事實上終究是敗了,究其原因,筆者認為主要有四:
- QT移動開發社群太小,學習資料不足,生態不好;
- 官方推廣不利,支援不夠;
- 移動端發力較晚,市場已被其它動態化框架佔領(Hybrid和RN);
- 在移動開發中,C++開發和Web開發棧相比有著先天的劣勢,直接結果就是QT開發效率太低。
基於此四點,儘管QT是移動端開發跨平臺自繪引擎的先驅,但卻成為了烈士。
Flutter
Flutter是Google推出並開源的移動應用程式(App)開發框架,主打跨平臺、高保真、高效能。其採用Dart語言作為開發語言,開發者可以通過Dart語言開發Flutter App,一套程式碼可同時執行在iOS和Android平臺上。Flutter提供了豐富的元件、介面,開發者可以很快地為Flutter新增原生擴充套件。
Flutter與其他用於構建App的大多數框架不同,因為Flutter既不使用WebView,也不使用平臺(Android、iOS等)的原生控制元件。相反,Flutter使用自己的高效能渲染引擎來繪製Widget。這樣不僅可以保證在Android和iOS平臺上UI的一致性,而且也可以避免對原生控制元件依賴而帶來的限制及高昂的維護成本。
Flutter使用Skia作為其2D渲染引擎,Skia是Google的一個2D圖形處理函式庫,包含字型、座標轉換以及點陣圖都有高效能且簡潔的表現,Skia是跨平臺的,並提供了非常友好的API,目前Google Chrome瀏覽器和Android均採用Skia作為其繪圖引擎,值得一提的是,由於Android系統已經內建了Skia,所以Flutter在打包APK(Android應用安裝包)時,不需要再將Skia打入APK中,但iOS系統並未內建Skia,所以構建iPA時,也必須將Skia一起打包,這也是為什麼Flutter APP的Android應用安裝包比iOS應用安裝包小的主要原因。
但是Flutter也有不足之處,不支援動態下發程式碼和熱更新。
本章總結
本章主要介紹了目前移動開發中三種跨平臺開發技術和原生開發技術,現在我們從框架角度對比一下三種跨平臺開發技術:
技術型別 | UI渲染方式 | 效能 | 開發效率 | 動態化 | 框架代表 |
---|---|---|---|---|---|
H5+原生 | WebView渲染 | 一般 | 高 | ✔️ | Cordova、Ionic |
JavaScript+原生渲染 | 原生控制元件渲染 | 好 | 高 | ✔️ | RN、Weex |
自繪UI+原生 | 呼叫系統API渲染 | 好 | Flutter高,QT低 | 預設不支援 | QT、Flutter |
上表中動態化主要指是否支援動態下發程式碼和是否支援熱更新。值得注意的是Flutter的Release包預設是使用Dart AOT(提前編譯)模式編譯的,所以不支援動態化,但Dart還有JIT(即時編譯)或snapshot執行方式,這些模式都是支援動態化的,後續會介紹。
此文章整理於:book.flutterchina.club/