跨平台技術發展的三個階段
-
第一階段是混合開發的web容器時代
- 為了解決原生開發的高成本、低效率,出現了Hybrid混合開發
- 原生中嵌入依託於瀏覽器的WebView
- Web瀏覽器中可以實現的需求在WebView中基本都可以實現
- 但是Web最大的問題是,它的性能和體驗與原生開發存在肉眼可感知的差異
- 因此並不適用於對性能和用户體驗要求較高的場景
-
第二階段是以RN和Weex為代表的泛web容器時代
- RN對Web標準進行了功能裁剪
- 用户體驗更接近於原生了
- 由於進行了功能裁剪,所以RN對業務的支持能力還不到瀏覽器的5%
- 因此僅適用於中低複雜度的低交互類頁面。面對稍微複雜一點兒的交互和動畫需求,都需要通過調用原生代碼去擴展才能實現
-
第三階段是以Flutter為代表的自繪引擎時代
- Flutter是構建Google物聯網操作系統Fuchsia的SDK
- 它使用Dart語言開發APP
- 一套代碼可以同時運行在iOS和Android平台上
- Flutter採用自帶的Native渲染引擎渲染視圖,它是自己完成了組件渲染的閉環
- 而RN、Weex之類的框架,只是通過JavaScript虛擬機擴展調用系統組件,最後是由Android或者iOS系統來完成組件的渲染
下面來看一下幾類型的混合開發APP:
Web APP框架
ionic
Ionic框架是基於Web技術應用HTML、CSS以及Java技術進行智能設備APP開發的框架,Ionic框架是用來開發混合模式的移動APP開發框架;
優勢
- 全套的UI組件
Ionic框架很注重外觀的體驗,所以它提供了很多UI組件幫助開發者開發APP,比如:下拉刷新、標籤等。界面美觀,開發者能夠很快的上手,開發的APP都很實用。 - 代碼容易維護
Ionic框架是基於AngularJS,也就支持AngularJS的特點,遵循標準的代碼,維護代碼就很容易,能夠完美融合AngularJS - 支持跨平台
可以在主流的Android操作系統和ios操作系統上運行,或者其他的操作系統也可以支持 - 很多強大的命令行
- 強大的社區、框架適用範圍廣
能夠編譯成各個平台的應用程序
劣勢
- 內存佔用高
- 不適合做遊戲類型app
- web技術無法解決一切問題,對於比較耗性能的地方無法利用native的思維實現優勢互補,如高體驗的交互,動畫等
Cordova
Cordova提供了一組設備相關的API;通過這組API,移動應用能夠以JavaScript訪問原生的設備功能,如攝像頭、麥克風等;Cordova還提供了一組統一的JavaScript類庫,以及為這些類庫所用的設備相關的原生後台代碼;Cordova支持如下移動操作系統:iOS, Android,ubuntu phone os, Blackberry, Windows Phone, Palm WebOS, Bada 和 Symbian。
通信
-
通信原理
- 保存Cordova_plugin.js的 插件文件名字和地址
- 插件的API呼出時,通過調用Cordova的exec模塊將API的參數保存在CommandQueue的隊列中。 CALLBACK則保存在JS側的callbacks map裏面
- 添加一個空的iframe,iframe的src則指向gap://ready
- 3的iframe的src設置以後,NATIVE側UIWebviewDelegate#shouldStartLoadWithRequest則被呼出來
- Webview的Delegatet判斷gap://ready的情況下,則執行commandDelegate的處理
- commandDelegate則從JS側取出API的參數,內部實現則是通過 UIWebview#stringByEvaluatingJavaScriptFromString的返回值 取得CommandQueue裏面的參數轉換成JSON數據
- 根據6的插件,執行NATIVE定義的插件實例
- 插件中,有CALLBACK的情況下,成功失敗的結果通過UIWebview#stringByEvaluatingJavaScriptFromString執行JS,JS端則根據傳過來的CALLBACKID,從callbacks map取出回調函數並執行
-
通信方式
- iframe的方法(默認)
- xmlHttpRequest的方法(iOS5.x版本因為 -webkit-scroll的IFRAME有BUG,則推薦使用)
-
插件導入流程
-
Native
- APP啓動,MainViewController初始化之時,queue和command的DELEGATE初期化 - config.xml文件解析,插件名設置到數組,插件文件和插件名設置到pluginMap,屬性設置到setting - 在Webview類裏面,加載index.html,index.html裏面加載cordova.js、開始初期化 -
JS
- 加載cordova.js時、內部的事件設置模塊,NATIVE交互模塊,初期化模塊,插件加載 - 插件模塊是cordova_plugins.js文件定義的插件文件地址,文件名保存的MAP - deviceready事件發佈後,插件的API可以使用了 - 插件API執行後,模塊MAP將插件文件加載,執行exec函數 - 在index.html裏面添加一個空的iframe、指定src=gap://ready,通知到Nativie
-
優勢
- iOS和Android基本上可以共用代碼;
- 純web思維,開發速度快, 簡單方便,一次編碼,到處運行;
- 如果熟悉web開發,文檔很全, 系統級支持封裝較好,所有UI組件都是有html模擬,可以統一 使用;
- 可實現在線更新,允許動態加載web js;
- 文檔多,開發者多,遇到問題容易解決,技術成熟;
劣勢
- 佔用內存高一些;
- 不適合做遊戲類型app;
- web技術午無法解決一 切問題,對於比較耗能的地方無法利用native的思維實現優勢互 補,如高體驗的交互,動畫等。
Hybrid APP(Webview)
利用 安卓和 iOS 上的 webview 容器,APP 能夠執行 html、css 和 js 腳本,展示 web 頁面。如果需要原生功能就添加 bridge 供 java 調用。具有開發效率高、跨平台、支持動態發佈等特點,它是目前應用最廣泛最成熟的一種方案;
Webview通信
-
假跳轉的請求攔截(不建議)
- 假跳轉的請求攔截 就是由網頁發出一條新的跳轉請求,跳轉的目的地是一個非法的壓根就不存在的地址
- 比如:wbcst://testhost/action?params=xxx
- 模擬http協議網絡請求 scheme://host/action?params
- 客户端會無差別攔截所有請求,真正的url地址應該照常放過,只有協議域名匹配的url地址才應該被客户端攔截
-
JS調用方式
- a標籤跳轉
- location.href跳轉
- iframe跳轉
- 不建議使用,android系統對url參數做了字節限制,無法進行大數據的通信
-
彈窗攔截(不建議)
-
alert
- 彈出個提示框,只能點確認無回調
-
confirm
- 彈出個確認框(確認,取消),可以回調
-
prompt
- 彈出個輸入框,讓用户輸入東西,可以回調
- 不建議使用,會無差別的攔截所有前端的window彈窗
-
-
JS上下文注入(推薦)
-
iOS
- WKWebView scriptMessageHandler注入
-
android
- addJavascriptInterface注入
-
特點
- 不通過任何攔截的辦法,而是直接將一個native對象(or函數)注入到JS裏面,可以由web的js代碼直接調用,直接操作
-
WebView 渲染引擎設計的上的缺陷
- JS Execute,Layout, Paint 都在MainThread ,無法並行化。
- JS 的性能趕不上 Native Tookit 的 Java Dart Object-C 等編譯型語言,執行復雜邏輯時會卡頓。
- 渲染流水線非常長,導致瀏覽器對合成器動畫和非合成器動畫區分對待,非合成器動畫性能不佳。
- OpenGL 設計上是推薦單線程模型,一個 Context 同時只能運行一個線程使用。 GPU Thread 運行在單獨 GPU 進程, Render 進程無法訪問 GPU 進程的 OpenGL Context ,兩個進程無法 Texture 共享資源。 Render 進程只能輸出 Bitmap/Command Buffer 通過 IPC 傳遞給 GPU 進程,無法直接在 GPU 進程的 Open GL Context 做直接光柵化,難以充分發揮現代 GPU 的性能。
- 光柵化是異步進行的,進行慣性滾動時,會出現白屏,這個是 Webview 始終無法避免的問題。
- 設備平台眾多,需要兼容CPU渲染,無法進行 All In GPU 的設計。
優勢
- 跨平台
- 開發週期短、成本低
- 用户體驗良好
- 可以即時修復bug、動態發版
劣勢
- 仿原生iOS效果複雜
- 機型兼容性
ReactNative/Weex跨平台技術
這種技術最大化的複用前端的生態和 Native 的生態體系,把 Native View 的高性能組件積累輸出給前端的技術體系。此方案和瀏覽器的最大區別在於 Script 的執行和 Native View 渲染體系。
ReactNative
通信流程(OC)
- ①js調用OC模塊暴露出來的方法
- ②把調用方法分解為ModuleName、MethodName、arguments,在丟給MessageQueue處理
- ③把js的callback函數緩存在MessageQueue的一個成員變量裏面,同時生成一個CallbackID來代表callback;在通過保存在MessageQueue的模塊配置表把ModuleName、MethodName轉成ModuleID、MethodID
- ④把ModuleID、MethodID、CallbackID和其他參數傳給OC(JavaScriptCore)
- ⑤OC接到消息,通過模塊配置表拿到對於的模塊和方法
- ⑥RCTModuleMethod對js傳過來的參數進行處理
- ⑦OC模塊方法執行完,執行block回調
- ⑧調用第6步中RCTModuleMethod生成的block
- ⑨block帶着CallbackID和block傳過來的參數去掉用js裏的MessageQueue方法invokeCallbackAndReturnFlushedQueue
- ⑩MessageQueue通過CallbackID找到相應的js的callback方法
- ⑪調用callback方法,並把OC帶過來的參數一起傳過去完成回調
優勢
雖然不能做到一次編碼到處運行,但是基本上即使是兩套代碼, 也是相同的jsx語法, 使用js進行開發。用户體驗高於html, 開發效率較高
Flexbox佈局據説比native的自適應佈局更加簡單高效
劣勢
對開發人員要求較高,不是懂點web技術就行的,當官方封裝的 控件、API無法滿足需 求時就必然需要懂一些native的東西去 擴展,擴展性仍然遠遠不如web,也遠遠不如直 接寫Native Code。
Weex
實現原理
Weex 對外通過 Rax 和 Vue 前端框架進行功能輸出,前端框架下有一層 JS Framework 來實現 dom 的功能。 WeexCore 負責基礎的 Flex Layout ,然後通過 Component 分別對接到 Android/iOS 的 Platform Native View 體系。
優勢
- Android Native 採用更輕量級的渲染流水線,能更快更高效的的響應事件;
- RenderThread 直接操作 OpenGLContext ,進行 Direct GPU Raster ,充分發揮現代 GPU 的特性,提供高性能渲染和流暢的體驗;
- 部分耗時操作,如 Bitmap 上傳 Texture , TextureThread 上傳到 Share Open GL Context 中, Texture 完成後通知主線程進行繪製,通過 Share Open GL Context 與主線程共享 Texture 等資源。 WebView 只能在 Render Process 內部進行 Texture 的共享, RenderProcess 無法與 GPU Process 共享 Texture 等資源;
- Android Native 有 RecycleView ViewPager 等高級組件,每個高級組件都做了性能的最佳實踐;瀏覽器上的高級組件只能通過 JS 模擬實現,優化定製效率低;
- 瀏覽器流水線設計複雜,需要考慮到 PC 、手機、嵌入式設備等多種複雜的環境,不少設備上木有 GPU ,只能進行 CPU 渲染。無法像 Android Native 體系一樣進行 All In GPU 的體系設計,全面發揮現代 GPU 的性能。
劣勢
Weex 體系充分將 Native 的 View 體系輸出到前端體系中,在進行 Android/iOS Native View 的封裝過程中,存在不少難以逾越的障礙
Flutter自繪引擎
Flutter是Google發佈的一個用於創建跨平台、高性能移動應用的框架。Flutter和QT mobile一樣,都沒有使用原生控件,相反都實現了一個自繪引擎,使用自身的佈局、繪製系統
框架
基礎架構主要分為三個部分:
-
Framework
- 純 Dart實現的 SDK,類似於 React在 JavaScript中的作用
- 它實現了一套基礎庫, 用於處理動畫、繪圖和手勢
- 基於繪圖封裝了一套 UI組件庫
- 根據 Material 和Cupertino兩種視覺風格區分開來
-
Engine
- 純 C++實現的 SDK
-
包括
- Skia引擎
- Dart運行時
- 文字排版引擎等
- 它是 Dart的一個運行時,它可以以 JIT 或者 AOT的模式運行 Dart代碼
- 這個運行時還控制着 VSync信號的傳遞、GPU數據的填充等,並且還負責把客户端的事件傳遞到運行時中的代碼
-
Embedder
- Embedder是操作系統適配層
-
實現了
- 渲染Surface設置
- 線程設置
- 平台插件等平台相關特性的適配
渲染流程
- GPU的VSync信號同步給到UI線程
- UI線程使用Dart來構建抽象的視圖結構(這裏是Framework層的工作)
- 繪製好的抽象視圖數據結構在GPU線程中進行圖層合成(在Flutter Engine層的工作)
- 然後提供給Skia引擎渲染為GPU數據,最後通過OpenGL或者 Vulkan提供給 GPU
優勢
高生產效率。一套代碼可以開發出Android和iOS應用;Dart語 言優越性,使得同樣的 功能只需要很少的代碼;迭代更加方便, hot reload功能;
創建優雅的、高度可定製的用户界面。Flutter內置了對Material Design和Cupertino(iOS-favor)的UI組件庫;提供了可定製 的UI組件,不再受制於OEM控件的限制;
藉助可移植的GPU加速的渲染引擎以及高性能本地ARM代碼運行 時以達到跨平台的高質量用户體驗。
劣勢
Flutter採用Dart語言開發,屬於小眾語言,需要一切都要重新 學習。
橫向對比
| 對比內容 | ReactNative | weex | Flutter | Hybrid |
|---|---|---|---|---|
| 平台實現 | JavaScript | JavaScript | 原生編碼 | H5 |
| 引擎 | JSCore | JS V8 | Flutter Engine | Webview |
| 核心語言 | React | Vue | Dart | JavaScript |
| 打包bundle文件 | 默認單一文件比較大(可拆包) | 較小,多頁面多文件 | 不需要 | 前端JS、CSS一般CDN引用 |
| 跨平台 | 中 | 中 | 中上 | 上 |
| 熱更新 | 好 | 好 | 暫無方案 | 好 |
| 性能 | 中 | 中 | 中上 | 差 |