Hybrid-APP技术原理
源寶導讀:Hybrid-APP技術不僅具有“Native APP的良好交互體驗”同時也具備“Web APP跨平臺開發(fā)的優(yōu)勢”。既然Hybrid-APP有這么多優(yōu)勢,那么究竟什么樣的APP才算Hybrid App呢?本文將分享我們的技術研究成果。
一、什么是Hybrid-APP
狹義的Hybrid:
也是現(xiàn)在大家普遍認知的,Hybrid就是一種給 WebView 增加一些js通信可以調(diào)用原生API的方式。
廣義Hybrid:
前端的開發(fā)思路與客戶端原生的開發(fā)思路相結(jié)合。
通過原生的配合,把原本js or 前端開發(fā)做不到的事情做到了,用原生的方式增強了原本的前端技術能力。
WebView+Bridge、RN、weex、小程序。
? ? 我能否認為,只要是前端的開發(fā)思路與客戶端原生的開發(fā)思路相結(jié)合,就認為他是一種Hybrid?
? ? 通過原生的配合,把原本js or 前端開發(fā)做不到的事情做到了,用原生的方式增強了原本的前端技術能力,是否就是一種 Hybrid?
? ? 無論是WebView+Bridge也好,RN類似的原生渲染框架也好,小程序也好,某種意義上講,他們都算Hybrid?
Hybrid框架-基本能力
? ? 接下來我們來看一下,一個Hybrid框架所需要具備的基本能力:
跨平臺能力。
? ? 這也是Hybrid應用與原生應用相比最大的優(yōu)點,一次編寫隨處運行。
靈活的業(yè)務模塊擴展能力。
良好的調(diào)用原生功能的能力。
? ? 由于在APP中有些功能必須由原生端提供,所以還需要有良好的調(diào)用原生功能的能力。
快速更新迭代的能力。
? ? 使用原生技術開發(fā)的APP每次更新都需要上傳應用商店審核,但是使用Hybrid技術開發(fā)的應用可以繞過應用商店實現(xiàn)熱更新。
二、Hybrid-APP中通信方案
? ? 在Hybrid APP中最核心的技術就在于前端與客戶端如何通信,接下來我們看一下,js與native之間是如何通信的。
2.1、JS 調(diào)用 Native 的幾種方法
假跳轉(zhuǎn)的請求攔截
A標簽跳轉(zhuǎn)。
原地跳轉(zhuǎn)。
iframe跳轉(zhuǎn)。
彈窗攔截
alert()
prompt()
confirm()
JS上下文注入
蘋果JavaScriptCore注入。
安卓addJavascriptInterface注入。
蘋果scriptMessageHandler注入。
2.2、Native 調(diào)用 JS 的幾種方法
evaluatingJavaScript 直接注入執(zhí)行JS代碼
? ? JS是一個腳本語言,任何一個JS引擎都可以在任意時機直接執(zhí)行JS代碼,我們可以把任何Native想要傳遞的消息/數(shù)據(jù)直接寫進JS代碼中。
loadUrl 瀏覽器用’javascript:’+JS代碼做跳轉(zhuǎn)地址
? ? 在瀏覽器中,可以直接用’javascript:xxxx’來簡單的執(zhí)行一些JS代碼,這個方法只有安卓可以用,因為iOS必須先將url字符串生成Request再交給webview去load
WKUserScript WKWebView的 addUserScript 方法
? ? WKWebView官方提供了一個Api,可以讓WebView在加載頁面的時候,自動執(zhí)行注入一些預先準備好的JS
2.2.1、假跳轉(zhuǎn)的請求攔截
? ? 假跳轉(zhuǎn)的請求攔截 就是由網(wǎng)頁發(fā)出一條新的跳轉(zhuǎn)請求,跳轉(zhuǎn)的目的地是一個非法的壓根就不存在的地址。
? ? url地址由協(xié)議、域名、路徑、參數(shù)這么幾個部分構(gòu)成,我們可以構(gòu)建一條假的url:用協(xié)議與域名當做通信識別、用路徑當做指令識別、用參數(shù)當做數(shù)據(jù)傳遞。
? ? 客戶端會無差別攔截所有請求,真正的url地址應該照常放過,只有協(xié)議域名匹配的url地址才應該被客戶端攔截,攔截下來的url不會導致webview繼續(xù)跳轉(zhuǎn)錯誤地址,因此無感知,相反攔截下來的url我們可以讀取其中路徑當做指令,讀取其中參數(shù)當做數(shù)據(jù),從而根據(jù)約定調(diào)用對應的native原生代碼。
JS發(fā)起調(diào)用
? ? JS其實有很多種方式發(fā)起假請求,跟發(fā)起一個新請求沒啥兩樣,只要按協(xié)議約定 生成假請求地址,正常的發(fā)起跳轉(zhuǎn)即可,任何一種方式都可以讓客戶端攔截住。
客戶端攔截
安卓的攔截方式:shouldOverride UrlLoading。
UIWebView的攔截方式:webView: shouldStartLoadWithRequest :navigationType。
WKWebView的攔截方式:webView: decidePolicyForNavigationAction :decisionHandler。
2.2.2、彈窗攔截
JS發(fā)起調(diào)用
? ? 可以使用alert/confirm/prompt三種彈框,每種彈框都可以由JS發(fā)出一串字符串,用于展示在彈框之上,而此字符串恰巧就是可以用來傳遞數(shù)據(jù),我們把所有要傳遞通訊的信息,都封裝進入一個js對象,然后生成字典,最后序列化成json轉(zhuǎn)成字符串。
客戶端攔截
安卓的攔截方式:onJsPrompt。
UIWebView的攔截方式:不支持截獲任何一種彈框。
WKWebView的攔截方式:webView: runJavaScriptText InputPanelWith Prompt :balbala。
2.2.3、蘋果UIWebview JavaScriptCore注入
客戶端注入
? ? UIWebView可以通過KVC的方法,直接拿到整個WebView當前所擁有的JS上下文documentView.webView.mainFrame.javaScriptContext。
? ? 拿到了JSContext,一切的使用方式就和直接操作JavaScriptCore沒啥區(qū)別了,我們可以把任何遵循JSExport協(xié)議的對象直接注入JS,讓JS能夠直接控制和操作。
JS調(diào)用
? 在沒經(jīng)過客戶端注入的時候,直接使用調(diào)用callNativeFunction()會報 callNativeFunction is not defined這個錯誤,說明此時JS上下全文全局,是沒有這個函數(shù)的,調(diào)用無效。
? 在執(zhí)行完客戶端注入后,此時JS上下文全局對象下面,就擁有了這個callNativeFunction的函數(shù)對象,就可以正常調(diào)用,從而傳遞數(shù)據(jù)到Native。
2.2.4、安卓addJavascriptInterface注入
客戶端注入
? ? 安卓的WebView有一個接口addJavascriptInterface,可以在loadUrl之前提前準備一個對象,通過這個接口注入給JS上下文,從而讓JS能夠操作,這個操作方式很類似蘋果UIWebview JavaScriptCore注入,整個機制也差別不大。
JS調(diào)用
? ? 在android端注入的對象同樣也被掛載在JS上下文全局對象下面,直接訪問即可調(diào)用。
2.2.5、蘋果WKWebView scriptMessage Handler注入
客戶端注入
? ? 蘋果在開放WKWebView這個性能全方位碾壓UIWebView的web組件后,也大幅更改了JS與Native交互的方式,提供了專有的交互APIscriptMessageHandler
需要注意的是如果當前WebView沒用了,需要銷毀,需要先移除這個對象注入,否則會造成內(nèi)存泄漏,WebView和所在VC循環(huán)引用,無法銷毀。
JS調(diào)用
? ?這里不像前邊兩個注入一樣,直接注入到JS上下文全局對象里,addScriptMessageHandler方法注入的對象被放到了,全局對象下一個Webkit對象下面。
? ? 并且調(diào)用方式和之前的兩種方法也不同,前兩種都可以讓js任意操作所注入自定義對象的所有方法,而addScriptMessageHandler注入其實只給注入對象起了一個名字nativeObject,但這個對象的能力是不能任意指定的,只有一個函數(shù)postMessage。
2.2.6、evaluatingJavaScript 執(zhí)行JS代碼
? ? 前面也簡單介紹了一下,JS是一個腳本語言,可以在無需編譯的情況下,直接輸入字符串JS代碼,直接運行執(zhí)行看結(jié)果,這也是為什么在Chrome里,在網(wǎng)頁運行的時候打開控制臺,可以輸入各種JS指令的看結(jié)果的。
? ? 也就是說當Native想要調(diào)用JS的時候,可以由Native把需要數(shù)據(jù)與調(diào)用的JS函數(shù),通過字符串拼接成JS代碼,交給WebView進行執(zhí)行。
? ? Android/iOS-UIWebView /iOS-WKWebView,都支持這種方法,這是目前最廣泛運用的方法。
2.2.7、loadUrl 執(zhí)行JS代碼
? ? 安卓在4.4以前是不能用evaluatingJavaScript 這個方法的,因此之前安卓都用的是webview直接loadUrl,但是傳入的url并不是一個鏈接,而是以”javascript:”開頭的js代碼,從而達到讓webview執(zhí)行js代碼的作用。
2.2.8、WKUserScript 執(zhí)行JS代碼
? ? 對于iOS的WKWebView,除了evaluatingJavaScript,還有WKUserScript這個方式可以執(zhí)行JS代碼,他們之間是有區(qū)別的,這個雖然是一種通信方式,但并不能隨時隨地進行通信。
evaluatingJavaScript 是在客戶端調(diào)用的時候js端會立刻執(zhí)行代碼。
WKUserScript 是預先準備好JS代碼,當WKWebView加載Dom的時候,執(zhí)行當條JS代碼。
2.3、JS主動調(diào)用Native的方案對比
三、cordova運行原理
? ? 在了解了Hybrid app核心的通信方案之后,我們接下來看看目前公司使用最廣泛的跨平臺技術cordova的運行原理是怎么樣的。
3.1、客戶端入口
? ? 這里客戶端以Android端為例分析,Android端默認的入口是mainActivity類,我們可以看到它其實繼承CordovaActivity類,一切初始化條件是從loadUrl方法開始。
3.2、Android端核心類CordovaActivity
? ? CordovaActivity內(nèi)依賴一個WebView類,一個Preferences類,一個CordovaInterface接口,并同時初始化一些配置信息。WebView具體實現(xiàn)是由CordovaWebViewImpl類,CordovaInterface接口具體實現(xiàn)是由CordovaInterfaceImpl類實現(xiàn)。
? ? CordovaWebViewImpl是核心類,里面會把一些插件能力初始化,用一個PluginManager進行管理,包含一個引擎類—CordovaWebViewEngine,這個引擎是通過反射的方式創(chuàng)建,自身初始化的時候把NativeToJsMessageQueue關聯(lián)起來,里面包含著以Js字符串為主的雙向鏈表,把每次從前端通過JS代碼存儲起來,然后通過綁定的橋接方式Pop出到相應的Native代碼中去。
? ? 最終實現(xiàn)由SystemWebViewEngine類來對Android系統(tǒng)中WebView控件進行二次包裝,這個類的初始化是在CordovaWebViewImpl類反射創(chuàng)建,相關插件和消息傳遞也是通過SystemWebViewEngine進行綁定。
3.3、JS端cordova初始化
? ? Android端調(diào)用loadUrl后會啟動webview加載前端代碼,首先會加載運行cordova.js中的代碼,在cordova.js中會運行cordova/init模塊對cordova進行一個初始化,初始化中主要的核心操作就是:檢查監(jiān)聽核心事件是否觸發(fā)、平臺初始化工作、加載插件js。
3.4、JS端平臺啟動處理
? ? 不同的平臺中平臺啟動處理的模塊會有一些差異,但是核心處理相差不大,在Android平臺中主要進行了三個處理:初始化通信模塊、處理物理按鍵的事件、在onCordovaReady事件被觸發(fā)時通知原生端展示webview。
3.5、JS端插件加載處理
? ? 插件js加載處理中主要先會通過load方法加載cordova_plugins.js,獲取到項目中用到的插件,然后通過injectScript方法加載插件js,可以看到整個加載過程都是通過添加script標簽進行加載的,所以一旦插件數(shù)量很多對加載速度會有一定的影響。
? ? 這里只是加載插件的js代碼,原生端的插件加載并不是在這里進行的。
3.6、Cordova啟動流程
? ? 最后來總結(jié)一下整個cordova的啟動流程,主要做了三個大的事情:
原生端啟動webview加載前端代碼。
初始化插件。
建立通信通道。
3.7、Cordova通信流程
? ? 啟動cordova后,在項目運行的過程中當前端需要調(diào)用native的能力時,就需要與native端進行通信,Cordova通信流程中主要有這么幾點:
Cordova通過在原生端與js端維護兩個消息隊列來處理消息回調(diào)。
Cordova在執(zhí)行完exec()后,android會馬上從消息隊列中取出數(shù)據(jù)同步返回,但不一定就是該次請求的數(shù)據(jù)。
js端通過輪詢獲取原生端消息隊列中的數(shù)據(jù)。
四、總結(jié)
? ? Hybrid 的宗旨就是,如果 WebView 本身做不到,或者做起來有很大限制或者性能不佳,那么可以讓原生配合,一起做到。
? ? 因為Hybrid本是一個面向業(yè)務服務的東西,如果業(yè)務的野心足夠大,WebView 容器的想象空間應該是在能力上與RN/小程序看齊的,WebView 在 Hybrid 的支持下,不單純是使用Bridge 調(diào)用幾個原生 API 的事。
? ? 我們完全可以拆解RN中的每個環(huán)節(jié),把RN號稱比 WebView 好的原生渲染/原生組件拆解融入 WebView,我也可以學習小程序保持 WXML/WXSS的開發(fā)方式,而非RN那樣統(tǒng)一用JSX開發(fā)。
? ? 這種拆解不是將所有框架優(yōu)點塞在一個大而全的框架中,各種優(yōu)化方案的選擇背后一定帶來的是一些取舍。誰來決定取舍,業(yè)務決定,如果自己能深度把握這里面的設計思想,就不用在乎什么新的輪子新的框架,取其設計優(yōu)點,融入自己的業(yè)務之中即可。
------ END ------
作者簡介
李同學:?前端研發(fā)工程師,目前負責天際移動平臺的相關研發(fā)工作。
也許您還想看
微前端架構(gòu)在容器平臺的應用
前端數(shù)據(jù)層落地實踐
移動建模平臺元數(shù)據(jù)存儲架構(gòu)演進
AI云店小程序演變之路
基于 Go 的微服務運行情況監(jiān)控實踐
總結(jié)
以上是生活随笔為你收集整理的Hybrid-APP技术原理的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何在 C# 8 中使用 Index 和
- 下一篇: 微软开源AI诊断工具Error Anal