jsbridge初探

jsbridge是隨着Hybrid App的流行而產生的一種技術。那麼Hybrid App是啥?Hybrid App又稱混合App,即同時使用了前端web技術(js,css,html)和原生native技術(java,kotlin,swfit,object-c)進行開發的移動應用。javascript

混合開發的優缺點

  • 優勢:開發快,易更新,開發週期短,跨平臺
  • 缺點:性能問題,兼容性問題

常見的混合開發框架

  • webview渲染:Cordova,uni-app
  • 原生渲染:React Native,Weex,Flutter
  • 混合渲染:小程序

jsbridge

如今不少App的頁面,不必定都是原生實現的,多是經過webview直接加載一個線上的h5站點。好比打開某粉紅App的會員購頁面,其實就是個移動端的網站。css

這麼一說,好像和混合開發也沒啥聯繫。不過你仔細看下頁面的右上角,會發現有個分享按鈕:點擊分享圖標,能夠把當前頁面分享到第三方平臺,分享後,web頁面須要知道是否分享成功。html

這裏就涉及了native端和web端的通訊:native分享的內容,須要web端的js進行設置(js -> native);native分享成功後,須要把消息通知給js(natvie -> js)。爲實現兩端的雙向通訊機制,就須要jsbridge技術了。前端

Native通知JS

由於h5網頁是經過原生端的webview加載的,因此原生端對當前網頁擁有很高的權限:Native端能夠直接在當前webview裏執行js代碼。java

// web端
function nativeCallback(data) {
  console.log('data', data);
}

咱們在js的執行環境裏定義了一個全局方法nativeCallback,native端能夠直接執行nativeCallback(123)方法,也就把數據傳給了js。web

這種方案是否是有點熟悉,jsonp就是相似的原理:只不過調用全局方法的時機,從服務器端改爲了native端。npm

JS通知Native

前端常見的協議有:json

  1. http/https協議:https://www.baidu.com
  2. 本地file協議: file:///Users/deepred/myproject/index.html

其實咱們也能夠自定義協議:sslocal://openModal?text=hello,客戶端經過分析這段scheme就能知道web端要調用原生的哪些方法,同時數據也經過query參數進行了傳遞。小程序

那web端如何發送這段scheme給native端呢?服務器

  • 攔截console alert prompt 全局方法。
alert('sslocal://openModal?text=hello')

native能夠攔截webview中的這些方法,從而調用原生方法。

  • 攔截url請求
const ifr = document.createElement('iframe');
ifr.style.display = 'none';
ifr.src = 'sslocal://openModal?text=hello';
document.body.appendChild(ifr);

web端加載了一個iframe,請求了sslocal://openModal?text=hello, native端經過攔截url請求,從而調用原生方法。

使用scheme字符串來調用方法始終不夠直觀,其實咱們還能夠向webview裏注入一個js全局對象,這個全局對象擁有調用native的方法的能力。

  • API注入
// nativeApp是由native端注入的全局變量
nativeApp.openModal('hello');

雙向通訊

前面咱們介紹的幾種方法,都只能單向通訊。如何進行雙向通訊呢?這時候就須要前端本身實現一個JS-SDK,維護js回調函數的Map。

首先,咱們假設客戶端會向webview中注入一個全局對象BILIAPP

// BILIAPP是原生端注入的
const BILIAPP = {
  invoke(methodName, param, onSuccessKey, onFailKey) {}
}

該對象有個invoke方法,接收4個參數:

  • 調用的原生方法名
  • 方法參數
  • 成功回調函數id
  • 失敗回調函數id

咱們無法直接傳函數給原生方法,因此這裏只能傳回調函數的id,id對應的實際函數,由前端這邊維護。

sdk.js

let id = 1;

const uuid = () => {
  return `callback_${id++}`;
};

// BILISDK是web端注入的
const BILISDK = {

  // key是回調函數的id
  // value是回調函數的值
  callbacks: {

  },

  // 暴露給前端使用的方法,支持Promise
  invokeP(methodName, param) {
    return new Promise((resolve, reject) => {
      const successCb = (data) => {
        resolve(data);
      };
      const failureCb = (data) => {
        reject(data);
      };

      return BILISDK._invoke(methodName, param, successCb, failureCb);
    });
  },

  // 實際真正調用原生對象的方法
  _invoke(methodName, param, successCb, failureCb) {
    const onSuccessKey = uuid();
    const onFailKey = uuid();
    // 存入callbacks hash表中
    this.callbacks[onSuccessKey] = successCb;
    this.callbacks[onFailKey] = failureCb;
    // BILIAPP是否注入成功
    BILIAPP && BILIAPP.invoke && BILIAPP.invoke(methodName, JSON.stringify(param), onSuccessKey, onFailKey);
  },

  // 暴露給原生端使用的方法
  invokeFromNative(key, param) {
    if (typeof param === "string") {
      try {
        param = JSON.parse(param)
      } catch (ex) {

      }
    }
    const callback = this.callbacks[key];

    if (callback) {
      callback(param);
    }
  }

}

// 使用BILISDK調用原生方法
BILISDK.invokeP('getVersion').then((res) => {
  console.log('res', res);
})

如今前端調原生方法,不要直接使用BILIAPP.invoke,而是經過BILISDK.invokeP間接調用。BILISDK.invokeP支持Promise化,同時維護了一個hash表

const BILISDK = {
  callbacks: {
    'callback_1': function() {},
    'callback_2': function() {},
  },
}

BILISDK.invokeFromNative是暴露給Native端使用的。當原生方法調用完成後,根據成功仍是失敗,Native端能夠調用BILISDK.invokeFromNative(成功或者失敗的id),而這個id就是當初BILIAPP.invoke調用時傳進來的id。

經過上面的方法,咱們就實現了js -> native -> js 的雙向通訊了。固然理論上,咱們還需實現:native -> js -> native 的雙向通訊,可是原理是同樣的,這時客戶端就須要本身實現一個Native-SDK,維護Native端回調函數的Map。

JS-SDK的接入

前面咱們實現的sdk.js,如何引入web站點呢?

把sdk打包成umd規範的js靜態文件,上傳到cdn或者發佈到npm

  • 在index.html裏面直接經過script標籤引入或者js直接import導入便可。該方案,前端維護sdk。(維護成本高)
  • 客戶端在初始化一個WebView打開頁面時,直接注入sdk。該方案,客戶端維護sdk。(優先推薦)

參考

  1. Hybrid App技術解析 -- 原理篇
  2. 小白必看,JSBridge 初探
  3. 2小時搞定移動端混合開發基礎入門
相關文章
相關標籤/搜索