狹義Hybridjavascript
也是如今你們廣泛認知的,Hybrid就是一種給 WebView 增長一些js通訊能夠調用原生API的方式前端
廣義Hybridjava
我可否認爲,只要是前端的開發思路與客戶端原生的開發思路相結合,就認爲他是一種 Hybrid?git
我可否認爲,經過原生的配合,把本來js or 前端開發作不到的事情作到了,用原生的方式加強了本來的前端技術能力,是否就是一種 Hybrid?github
我可否認爲,不管是 WebView+Bridge 也好,RN相似的原生渲染框架也好,小程序也好,某種意義上講,他們都算 Hybrid?web
由於Hybrid本是一個面向業務服務的東西,若是業務的野心足夠大,WebView 容器的想象空間應該是在能力上與RN/小程序看齊的,沒錯,WebView 在 Hybrid 的支持下,不單純是設計幾個 Bridge 調用幾個原生 API 的事,我其實在很多聊天羣裏深度聊過這個話題,泛前端動態化這個方向上,各類技術輪子都是一脈相承通着的,因此你看RN or 小程序是一個大廠新作的重型輪子?在我看來他們都是一回事,徹底能夠拆解RN中的每一個環節,把RN號稱比 WebView 好的原生渲染/原生組件拆解融入 WebView,我也能夠學習小程序保持 Html/CSS/JS 的開發方式(固然我知道小程序是WXML/WXSS),而非RN那樣統一用JSX開發。我甚至還能把RN與小程序都沒有的動態bridge融入到Hybrid容器中去,What’s more? 還有更多能夠開放的腦洞。數據庫
這種拆解不是說能夠作到把全部框架優勢塞在一個大而全的框架裏就完事的,各類優化方案的選擇背後必定帶來的是一些取捨。誰來決定取捨,業務決定,若是本身能深度把握這裏面的設計思想,就不用在意什麼新的輪子新的框架,取其設計優勢(優勢必定帶來取捨,若是選擇這個有點意味着也要選擇他的取捨),融入本身的業務之中。json
一個標準的WebView容器要具有哪些基礎的功能需求,來知足常規的 hybrid-webview 開發呢?小程序
Android 調用 JS 有兩種方式,都是經過
WebView
的方法:
bash
webview.loadUrl()
webview.evaluateJavascript()
兩者區別:
loadUrl()
會刷新頁面,evaluateJavascript()
則不會使頁面刷新,因此evaluateJavascript()
的效率更高
loadUrl()
得不到 js 的返回值,evaluateJavascript()
能夠獲取返回值
evaluateJavascript()
在 Android 4.4 以後纔可使用在
methods
中定義一個供 Android 調用的方法callJsFunction(str)
, 並可接收一個參數str
,而後改變頁面中的文字。若是隻是在
methods
中定義方法,原生調用會找不到這個方法。因此要在頁面加載的時候將方法掛載在window
上,這樣WebView
就能夠拿到此方法了。注意,這步很重要必定要寫!loadUrl() 實現:tbsWebView.loadUrl("javascript:callJsFunction('soloname')");
evaluateJavascript() 實現:
tbsWebView.loadUrl("javascript:callJsFunction('soloname')");
最基礎功能,WebView 各類想要調用原生能力都經過這個設計來通知原生,不管是打開新頁面新路由,仍是彈個 Tips 框,仍是執行 IAP 購買,仍是打開攝像頭等等。
對於JS調用Android代碼的方法有3種:
WebView
的 addJavascriptInterface()
進行對象映射WebViewClient
的 shouldOverrideUrlLoading()
方法回調攔截 urlWebChromeClient
的onJsAlert()
、onJsConfirm()
、onJsPrompt()
方法回調攔截JS對話框alert()
、confirm()
、prompt()
消息新建類 JsJavaBridge
public class JsJavaBridge {
private Activity activity;
private WebView webView;
public JsJavaBridge(Activity activity, WebView webView) {
this.activity = activity;
this.webView = webView;
}
@JavascriptInterface
public void onFinishActivity() {
activity.finish();
}
@JavascriptInterface
public void showToast(String msg) {
ToastUtils.show(msg);
}
}
複製代碼
而後經過 WebView
設置 Android
類與 JS 代碼的映射
tbsWebView.addJavascriptInterface(new JsJavaBridge(this, tbsWebView), "$App");
複製代碼
這裏將類 JsJavaBridge
在 JS 中映射爲了 $App
,因此在 Vue 中能夠這樣調用 $App.showToast("哈哈,我是js調用的")
。
仍是在基礎功能之上,若是 WebView 是想要獲取一些只有原生纔有的數據,好比讀原生數據庫,查看原生設備網絡/磁盤等硬件情況,須要在上面的功能下還額外回調給 WebView。
對JS消息體進行改造,增長用於處理回調相關的數據字段 callbackId 與 callbackFunction
對 callbackId 與 callbackFunction 進行賦值,callbackId 是一個保證每次通訊都惟一的一個id值 getNextCallbackID ,大概思路能夠是用時間戳+必定程度的隨機小數來進行生成,思路不深刻展開了。 callbackFunction 這裏咱們先寫 window.callbackDispatcher
後面會提到這個入口是怎麼操做的。
這裏有一步最最重要的操做就是,this.msgCallbackMap[callbackid] = callback;
會把 JS 業務的回調函數,保存在一個全局可處理的回調字典之中,而 Key 就是這個惟一ID callbackId,這樣當 客戶端發起回調的時候,你才能找到對應的 JS Function
咱們會按着下面這種方式去拼接 JS 而後用 evaluateJavaScript:
來注入調用JS,在客戶端拼接出一段 JS 代碼,但若是 params 這個數據中存在必定特殊字符好比 \r \n \f
等等,這些特殊字符會破壞 JS 的代碼結構,打破本來的 JS 語法,這塊要很是當心,尤爲是你要傳遞大型嵌套字典數據的時候,簡單的測試數據這個問題是沒法暴露出來的,若是 JS 代碼結構被破壞,那麼全部通訊 JS 的方法就失效了。Native會把 callbackId callbackFuction ResultString 拼接成以下 JS 代碼,注入回 WebView
window.callbackDispatcher('12345callbackid','{\'result\':\'result\'}');複製代碼
那麼前端要作的就是準備好對應的函數,在window的對象上,掛上 callbackDispatcher 這個函數,這就是爲啥我一開始說 callbackFunction 寫死 window.callbackDispatcher
的緣由,客戶端用這個字符串,拼出了 JS 代碼,這個 JS 代碼執行的時候,就恰好window下有這麼一個函數接着
window.callbackDispatcher: function (callbackId, resultjson) {
var handler = this.msgCallbackMap[callbackId];
if (handler && typeof (handler) === 'function') {
console.log(resultjson);
var resultObj = resultjson ? JSON.parse(resultjson) : {};
handler(resultObj);
}}複製代碼
當Native已經成功回調到 JS 了,那麼就用 callbackId 在剛纔保存的回調字典裏找到要回調的方法,而後把傳過來的 resultjson 用 JS 的 JSON.parse 反序列化成字典,而後用找到的回調方法把數據傳遞進去。@