摘要:隨著移動互聯(lián)網(wǎng)應(yīng)用大量投入市場,每天都有很多各行各業(yè)應(yīng)用產(chǎn)品被開發(fā)出來,這就要求應(yīng)用的開發(fā)效率被不斷提升。由于現(xiàn)有流行的原生客戶端IOS和Android開發(fā)周期較長,平臺一致性有差異,雙端開發(fā)人效高。那么可能會用到Web相關(guān)技術(shù)(HTML5、JavaScript、CSS3)作為替代原生開發(fā)的一種方案,此時會涉及原生應(yīng)用和網(wǎng)頁之間的交互和通信,而通信原理就顯得尤為重要。原生的交互和通信一般會有比如設(shè)置頂部導(dǎo)航欄標(biāo)題,播放聲音、拍攝照片、地理定位、聲音和傳感器等功能。該文通過分析當(dāng)前原生和H5之間多種通信方式的優(yōu)缺點(diǎn)以及目前主流的跨端方案,給出一個較為合理的混合開發(fā)通信交互方案。
關(guān)鍵詞:移動互聯(lián)網(wǎng);混合開發(fā)(Hybird)通信;? schema協(xié)議;React Native
中圖分類號:TP311? ? ? 文獻(xiàn)標(biāo)識碼:A
文章編號:1009-3044(2022)19-0040-02
最早的混合開發(fā)解決方案要追溯到Apache Cordova[1](PhoneGap)時代,當(dāng)時最主要解決的問題就是讓開發(fā)者們可以在網(wǎng)頁中能夠調(diào)用到IOS、Android等智能手機(jī)的核心功能,例如地理定位、聯(lián)系人、拍攝照片等。那么必然就要和原生Native通信和交互,而通信和交互又是混合開發(fā)中比較重要的、比較偏基礎(chǔ)底層實現(xiàn)的一個環(huán)節(jié)。一個具有高可用性,高性能的通信過程封裝庫可以在實際的業(yè)務(wù)開發(fā)或項目實踐中很好地避免問題的發(fā)生。那么高頻通信且偏底層的方案必然就存在一定約定規(guī)范,所以一個標(biāo)準(zhǔn)的、規(guī)范化、通用的跨語言規(guī)范或者協(xié)議就尤為重要。本文就針對以上提出的問題先進(jìn)行基于兩端Android和IOS基礎(chǔ)底層的通信方法的探索和梳理,同時給出一個較為合理的規(guī)范協(xié)議通信和交互的標(biāo)準(zhǔn)。
1 原生Android和網(wǎng)頁(HTML)之間的通信方式
在Android中如果希望提供Web應(yīng)用或者網(wǎng)頁功能,通常是使用WebView[2]執(zhí)行該操作。因為WebView類是Anrdoid的View類的擴(kuò)展。所以通信就是Android里(Kotlin或Java)的代碼和WebView中的JavaScript代碼進(jìn)行通信。那么通常會存在Android代碼調(diào)用JavaScript代碼或者JavaScript代碼調(diào)用Android代碼。
1.1 Android代碼調(diào)用JavaScript代碼
在Activity里OnCreate()方法中向應(yīng)用添加WebView實例,然后使用loadUrl方法加載網(wǎng)頁或者通過loadData方法去加載HTML字符串。這里可以理解成webview就是瀏覽器,Android可以像瀏覽器控制臺或eval一樣執(zhí)行前端的JS代碼。
1.2 JavaScript代碼調(diào)用Android代碼
常見有兩種方式,方式一是在使用WebView時,可以在JavaScript代碼和客戶端Android代碼之間創(chuàng)建新接口,該接口的實現(xiàn)就是Android代碼側(cè)調(diào)用addJavascriptInterface(),并傳入類實例以綁定到JavaScript以及JavaScript可調(diào)用以訪問類的接口名稱。然后JavaScript側(cè)就可以直接調(diào)用接口名稱下的實例方法了。方式二是通過攔截網(wǎng)頁的URL地址實現(xiàn)的通信,比如可以重載WebViewClient的shouldOverrideUrlLoading()方法,來判讀當(dāng)前請求的URL是否需要做特殊處理邏輯。注意這里的URL不僅限于網(wǎng)頁也可以是各種靜態(tài)資源文件比如圖片、音頻等。
2 原生IOS和網(wǎng)頁(HTML)之間的通信方式
在IOS中承載網(wǎng)頁的有UIWebView[3]和WKWebView[4],UIWebView目前已廢棄,該文暫不做闡述,以下的webview均指WKWebView。WKWebView是IOS8才出現(xiàn)的。WKWebView和JavaScript代碼的通信過程方式和Android的WebView基本一致,這里省略過程圖。
2.1 IOS代碼調(diào)用JavaScript代碼
主要用到WKWebView的方法evaluateJavaScript[5],有三個重載方法,比較常用三個參數(shù)同時帶返回值的。具體是用到了wkwebview中的evaluateJavaScript方法。
2.2 JavaScript代碼調(diào)用IOS代碼
IOS也有兩種方式,方式之一也是攔截URL地址請求,然后做不同處理來進(jìn)行通信,核心實現(xiàn)是基于對WKNavigationDelegate中的WKNavigationAction的實現(xiàn)。方式二是利用基于webkit內(nèi)核的瀏覽器的JavaScriptCore去做IOS代碼和JavaScript代碼之間接口實現(xiàn),因為JavaScriptCore[6]有JSVirtualMachine、JSContext和JSValue,這里主要是利用JSContext創(chuàng)建一個JavaScript代碼執(zhí)行環(huán)境然后利用JSVirtualMachine提供的能執(zhí)行JavaScript代碼的虛擬機(jī),然后執(zhí)行處理JSValue具體的值。
3 通信會遇到的問題以及解決方案思路
通過以上通信方式介紹,1.2中的方式一和2.2中的方式二顯然對于雙端(Native和HTML)都有比較強(qiáng)深的侵入性,不利于工程化結(jié)構(gòu)。而作為瀏覽器網(wǎng)頁的基礎(chǔ)能力,對于URL的攔截和直接執(zhí)行JavaScript代碼顯然簡單,高效。但實際會遇到很多問題。比如雙方通信的數(shù)據(jù)結(jié)構(gòu)怎么定義,有回執(zhí)的消息如何實現(xiàn),cookie狀態(tài)如何同步,安全漏洞[2]等。
3.1通信數(shù)據(jù)結(jié)構(gòu)如何定義
探討以URL為基礎(chǔ),定義協(xié)議規(guī)則比如:schema://host/path?query其中schema定義插件名字或APP協(xié)議,客戶端僅接收內(nèi)置于代碼中的schema,比如fb。這樣范圍可控。host用來區(qū)分模塊比如訂單模塊、用戶模塊等含義。path用來定義模塊里的子項比如訂單的詳情、訂單的列表。query子項攜帶的額外查詢參數(shù),比如id。完整的結(jié)構(gòu)定義例子如:fabcd://order/detail?id=1234。
3.2 Cookie如何同步狀態(tài)到HTML中
常用的是CookieManager,其作用主要有兩點(diǎn),一是cookie可以實現(xiàn)同步到WebView或WKWebView中;二是可以請求時攜帶cookie信息。
3.3通信消息回執(zhí)如何實現(xiàn)
核心原理是傳遞一個全局唯一的隨機(jī)數(shù)作為雙方接口的方法名字,JavaScript提前注冊該具名函數(shù),以字符串形式傳遞到Native側(cè),在適當(dāng)?shù)臅r機(jī)執(zhí)行該具名函數(shù)。這種模擬全雙工通信,需要考慮消息時序[3],構(gòu)建消息隊列,考慮限流、節(jié)流、冪等發(fā)送消息。
3.4頁面棧問題考慮
頁面棧會有諸多問題,比如離線時導(dǎo)航欄如何顯示HTML的title文本,是否可以在網(wǎng)絡(luò)錯誤時不顯示白屏網(wǎng)頁,點(diǎn)擊重試按鈕就可以重試下載離線HTML資源,HTML頁面間如何實現(xiàn)傳參,如何實現(xiàn)自定義是否返回等。基于以上問題,方案一是禁止HTML里的跳轉(zhuǎn),統(tǒng)一走Native單獨(dú)創(chuàng)建WebView實例進(jìn)行跳轉(zhuǎn),優(yōu)勢是標(biāo)準(zhǔn)統(tǒng)一都有Native去控制,HTML不用去關(guān)心返回或者關(guān)閉,缺陷是硬件開銷大。方案二是用復(fù)雜的時序來確定由H5處理返回還是NA處理返回。具體是,當(dāng)用戶點(diǎn)擊了返回按鈕而非關(guān)閉按鈕時,NA調(diào)用JS的onBackClick。然后JS自己這邊依照業(yè)務(wù)邏輯或者自己的頁面棧是否為根來決定自己要如何處理。若需要關(guān)閉H5頁面,則調(diào)用NA close,若調(diào)用自行處理,則調(diào)用自己的history.back。并且調(diào)用NA的方法已處理的回調(diào)。若NA長時間未收到回調(diào),則自動關(guān)閉頁面。
4 目前流行的混合通信相關(guān)方案分析
4.1 React-Native
我們先從官網(wǎng)給出的定義開始了解,React-Native[7]是一個使用React和應(yīng)用平臺的原生功能來構(gòu)建Android和IOS應(yīng)用的開源框架。也就是可以使用JavaScript代碼(react代碼)訪問React Native的模塊,進(jìn)而最終調(diào)用原生模塊。這個我們上邊提到的JS代碼執(zhí)行NA的代碼,也就是1.2中的android利用addJavascriptInterface和ios利用JavaScriptCore來實現(xiàn)直接讓JS代碼調(diào)用到原生的方法會不會一樣呢,接下來就來對此分析一下。
4.1.1 Android端通信模型
首先Android的語言大多是Java,當(dāng)然還有kotlin,這里先基于Java。React-Native在Native端的框架實現(xiàn)就是用的Java語言,那么本質(zhì)上是Java和JavaScript兩種語言程序之間的調(diào)用。那么其實上述已經(jīng)說到了Webview之間的通信方式,但是React-Native和Webview沒啥關(guān)系,所以這里猜測可能是利用的是so庫,因為react-native必然依賴于webkit解析,而解析完了要和Java通信必然會找到一個中間的產(chǎn)物,也就是更往底層走,也就是C/C++做的一個bridge。
4.1.2 IOS端的通信模型
IOS和React-Native通信其實大致與Android相同,都是依賴于C/C++編寫的Bridge,不過React-Native最新的架構(gòu)是依賴于JSI,JSI核心要做的事就是對JS引擎與Native(C++)之間相互調(diào)用的封裝。下面簡單闡述下通信過程,首先js側(cè)會直接去調(diào)用已經(jīng)內(nèi)置好的NativeModules或者三方的NativeModules,那么就會進(jìn)入一個MessageQueue隊列中,這個隊列中存放是JS調(diào)用NativeModules方法的隊列,這里是異步通信的,因為JS本身是單線程,如果同步執(zhí)行,必然會阻塞后續(xù)JS執(zhí)行。這里的異步執(zhí)行時機(jī)也很重要,一般JS不會主動傳遞數(shù)據(jù)到OC,在調(diào)用OC方法時,會把ModuleID,MethodID等數(shù)據(jù)添加到一個隊列里,等OC過來調(diào)用JS的任意方法時,再把這個隊列返回給OC,此時OC再執(zhí)行這個隊列里要調(diào)用的方法。這樣的好處就是保證了JS側(cè)和Native側(cè)的事件和邏輯同步,同時JS也可以順便做call native,就避免JS和Native之間的頻繁通信了。
5 通信整體解決方案以及架構(gòu)圖
通過從最傳統(tǒng)的Aphche Cordova混合開發(fā)方式、自主探索混合開發(fā)通信,然后再到主流的跨端方案[4-5]探究其通信方案,探索出傳統(tǒng)混合開發(fā)通信[6]經(jīng)典方法是Native端會對URL進(jìn)行攔截,但這里的URL最好是滿足協(xié)議規(guī)范的schema規(guī)范,有host、子模塊、query、同時還有約定的消息格式加解密、cookie的管理、特定的消息時序等等,解析后處理完畢后不管處理成功還是失敗,都會執(zhí)行對應(yīng)回調(diào)方法通知前端JS側(cè),而JS側(cè)會提前被注入一個對象[7],此對象包含了一些NA的方法,有需要時就調(diào)用NA的方法并傳入對應(yīng)的協(xié)議scheme和與之關(guān)聯(lián)的參數(shù),比如處理完的回調(diào)等;整體架構(gòu)圖如圖1所示:
6 總結(jié)
本文重點(diǎn)先是闡述了早期的Android和IOS與HTML網(wǎng)頁之間通信的基本原理,知道基本原理再看現(xiàn)如今的跨端方案,?有很多實現(xiàn)通信方式也是基于基本的原理不斷進(jìn)行演化而來,因為基本的跨語言通信方式,要么跨進(jìn)程間通信,要么就通過更底層的語言(C/C++)實現(xiàn)中間語言傳輸通信交流。然而本身自主探索通信交互方案就會遇到一些世界要考慮的問題,比如協(xié)議制定、頁面棧管理、cookie狀態(tài)同步,消息時序等本文結(jié)合實踐經(jīng)驗給出一些實踐經(jīng)驗結(jié)論供參考,同時整理出理想的架構(gòu)方案圖。按照此基地接入至少能保證協(xié)議可維護(hù)、可擴(kuò)展。
參考文獻(xiàn):
[1] 王亞飛.Apache Cordova移動應(yīng)用開發(fā)實戰(zhàn)/跨平臺移動開發(fā)叢書[M].北京:清華大學(xué)出版社,2017.
[2] 柯芬芬.跨平臺移動應(yīng)用開發(fā)技術(shù)的安全性研究[J].無線互聯(lián)科技,2020,17(5):152-153.
[3] 鄒瓊俊.H5+跨平臺移動應(yīng)用實戰(zhàn)開發(fā)[M].北京:北京航空航天大學(xué)出版社,2019.
[4] 向治洪.React Native移動開發(fā)實戰(zhàn)2版[M].北京:人民郵電出版社,2020.
[5] 邱鵬源.React Native精解與實戰(zhàn)[M].北京:機(jī)械工業(yè)出版社,2018.
[6] 張靜,薛茹.基于JSBridge技術(shù)的跨平臺移動應(yīng)用開發(fā)研究[J].信息與電腦(理論版),2021,33(6):100-102.
[7] 邵增光.一種插件化JSBridge的實現(xiàn)方法:CN108804082A[P].2018-11-13.
收稿日期:2021-10-11
作者簡介:岳奎(1989—),男,陜西漢中人,本科,高級前端工程師,主要研究方向為大前端。