Hybrid前端jsbridge設計原理分析

kimi_he發表於2018-03-23

時下app分以下幾種技術選型

  1. Webapp,框架有cordova,ionic等
  2. Hybrid,即Native+h5方式
  3. React Native, weex方向
  4. 純Native,這個沒啥好說的
  5. flutter,明日之星 本文只描述Hybrid中的jsbrige部分實現原理,不會涉及Native部分的webview如何設計,關於webview的文章太多了,可以參考別的文章

通訊原理

預先定義好schema,如myapp://... 通過前端傳送偽連線請求iframe.src

        var iframe = document.createElement('iframe')
        iframe.style.display = 'none'
        iframe.src = schema
        var body = document.body
        body.appendChild(iframe)
        setTimeout(function () {
            body.removeChild(iframe)
            iframe = null
        })
複製程式碼

可以看到,在某個按鈕或者行為需要與原生端通訊時建立一個iframe然後再移除,如果不移除iframe,則會在body裡出現大量無用的iframe標籤,這裡簡單說一下Native怎麼去攔截這個偽連結請求。 在WebviewClient裡override一個方法:

    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
        if (url.startsWith(SCHEMA)) {
            ...
            return true;
        }
    }
複製程式碼

引數傳遞

實際專案中,肯定是要傳遞引數給Native接收的,可以對這樣封裝

        for (key in data) {
            if (data.hasOwnProperty(key)) {
                schema += '&' + key + data[key]
            }
        }
複製程式碼

這樣就可以拼接出&key=value這種形式,Native端可以寫一個方法獲取到key和value的值然後去做相應的處理。

方法回撥

有的時候bridge事件處理完之後需要告訴前端一些訊息回饋,那麼前端需要封裝一個callbackName傳給Native端

        var callbackName = ''
        if (typeof callback === 'string') {
            callbackName = callback
        } else {
            callbackName = action + Date.now()
            console.log('callbackName: ' + callbackName)
            window[callbackName] = callback
        }
        schema += 'callback=' + callbackName
複製程式碼

特別注意:這裡有一個Date.now(),作用是為了避免回撥方法重複,且避免在ios手機上不斷點選造成介面阻塞UI失去響應。

Native端執行回撥寫法:

    public static void call(WebView webview, String js) {
        if (webview != null) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                webview.evaluateJavascript(js, null);
            } else {
                webview.loadUrl("javacript:" + js);
            }
        }
    }
複製程式碼

用法舉例

jsbrige封裝完畢後,可以測試,以分享為例:

      window.bridge.share({
        title: '...',
        content: '...'
      }, function (result) {
        if (result.success) {
          alert('分享成功')
        }
      })
複製程式碼

基於vue的封裝

只需載入brige.js即可

import './bridge'
import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false

new Vue({
  render: h => h(App)
}).$mount('#app')
複製程式碼

總結

  1. 在ios上不要使用UIWebview,使用WkWebview。
  2. android要注意webview記憶體洩漏問題。
  3. 前端要控制打包體積,不推薦用webpack,因為會生成大量的webpack的封裝程式碼。
  4. 推薦使用rollup輕量級前端工程化打包,不會生成多餘的js程式碼。
  5. 推薦使用多頁模式,Native端通過pushwindow等方法,把跳轉權交給Native端。

相關文章