動態

詳情 返回 返回

WebViewJavascriptBridge.js代碼學習 - 動態 詳情

//notation: js file can only use this kind of comments
//since comments will cause error when use in webview.loadurl,
//comments will be remove by java use regexp
(function() {
  if (window.WebViewJavascriptBridge) {
    return;
  }
  
  var messagingIframe;
  var bizMessagingIframe;
  var sendMessageQueue = [];
  var receiveMessageQueue = [];
  var messageHandlers = {};
  
  var CUSTOM_PROTOCOL_SCHEME = 'yy';
  var QUEUE_HAS_MESSAGE = '__QUEUE_MESSAGE__/';
  
  var responseCallbacks = {};
  var uniqueId = 1;
  
  // 創建消息index隊列iframe
  function _createQueueReadyIframe(doc) {
    messagingIframe = doc.createElement('iframe');
    messagingIframe.style.display = 'none';
    doc.documentElement.appendChild(messagingIframe);
  }
  //創建消息體隊列iframe
  function _createQueueReadyIframe4biz(doc) {
    bizMessagingIframe = doc.createElement('iframe');
    bizMessagingIframe.style.display = 'none';
    doc.documentElement.appendChild(bizMessagingIframe);
  }
  //set default messageHandler  初始化默認的消息線程
  function init(messageHandler) {
    if (WebViewJavascriptBridge._messageHandler) {
      throw new Error('WebViewJavascriptBridge.init called twice');
    }
    WebViewJavascriptBridge._messageHandler = messageHandler;
    var receivedMessages = receiveMessageQueue;
    receiveMessageQueue = null;
    for (var i = 0; i < receivedMessages.length; i++) {
      _dispatchMessageFromNative(receivedMessages[i]);
    }
  }
  
  // 發送
  function send(data, responseCallback) {
    _doSend({
      data: data
    }, responseCallback);
  }
  
  // 註冊線程 往數組裏面添加值
  function registerHandler(handlerName, handler) {
    messageHandlers[handlerName] = handler;
  }
  // 調用線程
  function callHandler(handlerName, data, responseCallback) {
    _doSend({
      handlerName: handlerName,
      data: data
    }, responseCallback);
  }
  
  //sendMessage add message, 觸發native處理 sendMessage
  function _doSend(message, responseCallback) {
    if (responseCallback) {
      var callbackId = 'cb_' + (uniqueId++) + '_' + new Date().getTime();
      responseCallbacks[callbackId] = responseCallback;
      message.callbackId = callbackId;
    }
    
    sendMessageQueue.push(message);
    messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE;
  }
  
  // 提供給native調用,該函數作用:獲取sendMessageQueue返回給native,由於android不能直接獲取返回的內容,所以使用url shouldOverrideUrlLoading 的方式返回內容
  function _fetchQueue() {
    var messageQueueString = JSON.stringify(sendMessageQueue);
    sendMessageQueue = [];
    //android can't read directly the return data, so we can reload iframe src to communicate with java
    bizMessagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://return/_fetchQueue/' + encodeURIComponent(messageQueueString);
  }
  
  //提供給native使用,
  function _dispatchMessageFromNative(messageJSON) {
    setTimeout(function() {
      var message = JSON.parse(messageJSON);
      var responseCallback;
      //java call finished, now need to call js callback function
      if (message.responseId) {
        responseCallback = responseCallbacks[message.responseId];
        if (!responseCallback) {
          return;
        }
        responseCallback(message.responseData);
        delete responseCallbacks[message.responseId];
      } else {
        //直接發送
        if (message.callbackId) {
          var callbackResponseId = message.callbackId;
          responseCallback = function(responseData) {
            _doSend({
              responseId: callbackResponseId,
              responseData: responseData
            });
          };
        }
        
        var handler = WebViewJavascriptBridge._messageHandler;
        if (message.handlerName) {
          handler = messageHandlers[message.handlerName];
        }
        //查找指定handler
        try {
          handler(message.data, responseCallback);
        } catch (exception) {
          if (typeof console != 'undefined') {
            console.log("WebViewJavascriptBridge: WARNING: javascript handler threw.", message, exception);
          }
        }
      }
    });
  }
  
  //提供給native調用,receiveMessageQueue 在會在頁面加載完後賦值為null,所以
  function _handleMessageFromNative(messageJSON) {
    console.log(messageJSON);
    if (receiveMessageQueue) {
      receiveMessageQueue.push(messageJSON);
    }
    _dispatchMessageFromNative(messageJSON);
    
  }
  
  var WebViewJavascriptBridge = window.WebViewJavascriptBridge = {
    init: init,
    send: send,
    registerHandler: registerHandler,
    callHandler: callHandler,
    _fetchQueue: _fetchQueue,
    _handleMessageFromNative: _handleMessageFromNative
  };
  
  var doc = document;
  _createQueueReadyIframe(doc);
  _createQueueReadyIframe4biz(doc);
  var readyEvent = doc.createEvent('Events');
  readyEvent.initEvent('WebViewJavascriptBridgeReady');
  readyEvent.bridge = WebViewJavascriptBridge;
  doc.dispatchEvent(readyEvent);
})();

待過的某家公司中用於和原生交互的方案,當初這個文件也是從網上找的,拿來就用,沒怎麼細看。

最早我們的Hybrid應用只是h5調用原生的一些方法,所以只使用了native向頁面注入一個webview的變量,並在這個變量上掛載一些方法,在安卓上存在一些問題(?安全漏洞)。後來改用bridge,安全性和兼容性比較好。bridge有一個小小的問題就是如果原生端不存在對應名稱的方法,h5無法知道,所以h5需要做一套版本的判斷,再進行方法的調用。

執行的操作

簡單分析一下:這個文件包含了一個立即執行函數,代碼加載後,做了幾件事情:

  1. 定義協議scheme,類似http,這裏定義為yy
  2. 創建消息index隊列iframe,這個iframe後面可以看到是h5端使用的;創建消息體隊列iframe,這個iframe可以看到是給原生端使用的。這兩個iframe用於消息傳遞。
  3. 創建WebViewJavaScriptBridge對象並掛載到window上
  4. 創建Event對象,初始化事件名為WebViewJavaScriptBridgeReady的事件,並觸發事件,表示bridge對象已準備好,可以使用了。

bridge對象

bridge對象有6個方法,分別為init、send、registerHandler、callHandler、_fetchQueue、_handleMessageFromNative,方法的作用大致可以見名知意。

  • init方法,用於設置默認的消息處理函數,如果此時h5端的消息接收隊列不為空,則調用_dispatchMessageFromNative來分發處理隊列中的消息
  • send方法,用於h5端發送消息,觸發native去處理,調用_doSend將消息塞進h5端的消息發送隊列,並將messagingIframe的src設置為發送的協議,觸發iframe重新請求加載
  • registerHandler方法:用於定義消息名稱與消息回調函數的映射,保存在messageHandlers對象上,提供js函數給native調用,在原生完成操作後,將數據通過回調函數傳遞給h5
  • callHandler方法:用於h5調用_doSend修改iframe src的方式將消息和數據傳遞原生
  • _fetchQueue方法:提供給native使用的方法,將bizMessagingIframe的src設置為接收的協議,觸發iframe重新請求,使原生獲取到h5發送的消息
  • _handleMessageFromNative方法:提供給native使用的方法,將消息塞進h5的消息接收隊列,並進行消息分發

大致流程

1. H5調用Native

2. Native向H5傳遞

原理

H5調用Native的流程主要利用了iframe與原生端進行交互,通過修改iframe的src來觸發原生端調用對應的操作,原生端監聽到觸發後,經過一系列操作後得到一個結果,再通過調用h5的回調函數,使h5可以處理原生返回的結果。

Native向h5傳遞有點類似h5的事件監聽,h5註冊一個名稱用於標記操作,映射一個回調函數,原生端在用户做了某些操作後,獲取到h5註冊的操作對應的回調函數,並進行調用,將操作結果傳遞給h5。

參考

https://github.com/lzyzsd/JsB...

luffyjet/WebViewJavaScriptBridge

marcuswestin/WebViewJavascriptBridge

https://blog.csdn.net/weixin_...

https://blog.csdn.net/sinat_3...

Android與JS交互篇--JSBridge的使用

user avatar thosefree 頭像 littlelyon 頭像 nbidashuju 頭像 tizuqiudehongcha 頭像 wubomu 頭像 gssggssg 頭像 baozouai 頭像 ddup365 頭像 compose_hub 頭像 baqidemakebei 頭像 dexteryao 頭像 webshijie 頭像
點贊 23 用戶, 點贊了這篇動態!
點贊

Add a new 評論

Some HTML is okay.