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