前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >JSBridge 框架解析

JSBridge 框架解析

作者头像
前端小tips
发布2021-12-08 17:07:36
7320
发布2021-12-08 17:07:36
举报
文章被收录于专栏:前端文章小tips

非常需要注意的是关于安卓端 在assets文件夹里注入的js桥梁文件 一定不要有注释 或者是压缩成一行代码才能注入成功

代码语言:javascript
复制
// 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
// version 0.1.1
// write by shuchong
(function () {
  if (window.la) {
    return
  }
  // native和js的沟通,通过发送消息、接收队列、处理消息的逻辑来处理
  // js发给native用url
  // native发给js通用调用_handleMessageFromNative方法
  // 发送消息请求url的iframe
  var messagingIframe
  // js发送消息的队列
  var sendMessageQueue = []
  // js接收消息的队列
  var receiveMessageQueue = []
  // 接收消息处理的方法集
  var messageHandlers = {}
​
  var CUSTOM_PROTOCOL_SCHEME = 'yy'
  var QUEUE_HAS_MESSAGE = '__QUEUE_MESSAGE__/'
​
  // 发送信息给安卓后,安卓回调js的callback方法集
  var responseCallbacks = {}
  // 安卓回调js的callback方法集的id
  var uniqueId = 1
​
  // 创建发送消息的iframe
  function _createQueueReadyIframe (doc) {
    messagingIframe = doc.createElement('iframe')
    messagingIframe.style.display = 'none'
    doc.documentElement.appendChild(messagingIframe)
  }
​
  // set default messageHandler  初始化默认的接收消息队列
  // messageHandler为默认的js端收到消息的处理函数
  function init (messageHandler) {
    if (la._messageHandler) {
      throw new Error('WebViewJavascriptBridge.init called twice')
    }
    la._messageHandler = messageHandler
    var receivedMessages = receiveMessageQueue
    receiveMessageQueue = null
    for (var i = 0; i < receivedMessages.length; i++) {
      _dispatchMessageFromNative(receivedMessages[i])
    }
    console.log('la inited');
  }
​
  // 发送
  function send (data, responseCallback) {
    _doSend({
      data: data
    }, responseCallback)
  }
​
  // 注册线程 往数组里面添加值
  function registerHandler (handlerName, handler) {
    messageHandlers[handlerName] = handler
  }
  // JS调用Native方法时,通过该方法出发native的shouldOverrideUrlLoading方法,使Native主动向JS取数据
  // 调用线程
  // js调用native方法
  function callHandler (handlerName, data, responseCallback) {
    _doSend({
      handlerName: handlerName,
      data: data
    }, responseCallback)
  }
​
  // 3、JS将数据发送到Native端
  // sendMessage add message, 触发native的 shouldOverrideUrlLoading方法,使Native主动向JS取数据
  // *******************
  // 把消息队列数据放到shouldOverrideUrlLoading 的URL中不就可以了吗??
  // 为什么还要Native主动取一次,然后再放到shouldOverrideUrlLoading的URL中返回??
  function _doSend (message, responseCallback) {
    // 发送的数据存在
    if (responseCallback) {
      //
      var callbackId = 'cb_' + (uniqueId++) + '_' + new Date().getTime()
      responseCallbacks[callbackId] = responseCallback
      message.callbackId = callbackId
    }
    // 添加到消息队列中
    sendMessageQueue.push(message)
    // 让Native加载一个新的页面
    messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE
  }
​
  // 将数据返回给Native
  // 提供给native调用,该函数作用:获取sendMessageQueue返回给native,由于android不能直接获取返回的内容,
  // 所以使用url shouldOverrideUrlLoading 的方式返回内容
  function _fetchQueue () {
    // json数据
    var messageQueueString = JSON.stringify(sendMessageQueue)
    // message数据清空
    sendMessageQueue = []
    // 数据返回到shouldOverrideUrlLoading
    // android can't read directly the return data, so we can reload iframe src to communicate with java
    messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://return/_fetchQueue/' + encodeURIComponent(messageQueueString)
  }
​
  // 2、分发Native的消息
  function _dispatchMessageFromNative (messageJSON) {
    setTimeout(function () {
      // 解析消息
      // todo:try catch
      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 {
        // 消息中有callbackId 说明需要将处理完成后,需要回调Native端
        // 直接发送
        if (message.callbackId) {
          // 回调消息的 回调ID
          var callbackResponseId = message.callbackId
          //
          responseCallback = function (responseData) {
            // 发送JS端的responseData
            _doSend({
              responseId: callbackResponseId,
              responseData: responseData
            })
          }
        }
        // jsBridge的js端默认回调
        var handler = la._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)
          }
        }
      }
    })
  }
  // 1.收到Native的消息
  function _handleMessageFromNative (messageJSON) {
    //
    console.log(messageJSON)
    // 添加到接收消息队列
    if (receiveMessageQueue) {
      receiveMessageQueue.push(messageJSON)
    }
    // 分发Native消息
    _dispatchMessageFromNative(messageJSON)
  }
​
  function listen (eventName, handler) {
    callHandler(eventName)
    messageHandlers[eventName] = handler
  }
  //   mossSpeech
  // Array.<string>
  // 注册监听的语音识别文字。用户说该文字触发回调函数执行。
  // mossSkillset
  // Array.<string>
  // 注册监听的语义skillcommand。用户语义生成skillcommand如果命中设置的内容,则触发回调。
  //   wx.addMossEventListener({
  //     mossSpeech:['刷新支付二维码'],
  //     mossSkillset:['Search']
  // Tips:  当前后台语意注册需要两部分字段:intent/domain;
  // // 为保持参数一致,故Skillset形式约定为 ‘intent[]domain’,如:
  // // mossSkillset:['miniProgram[]search', 'generalControl[]nextPage']
  //   }, onSkillCommand)
​
  // words 为字符串数组, 最少有一个词
  function registerSpeech (words, handler) {
    callHandler('_registerSpeech', words)
    words.forEach(word => {
      messageHandlers['_speech' + word] = handler
    })
  }
​
  // init(messageHandler),初始化,设置js收到消息时的默认messageHandler,并消化所有init之前已接收的消息
  // send(data, responseCallback),js端发送消息给native
  // registerHandler,js端注册某native消息的处理方法,消息的handlerName,registerHandler(handlerName, handler)
  // callHandler,js端调用native方法,callHandler(handlerName, data, responseCallback)
  // listen, js端注册异步响应的消息处理方法listen(eventName, data, handler,isAdd=true),isAdd是否是增加的处理,false时清空之前的处理handler
  // 这个功能还没想好-------unListen(eventName,data), 取消监听(安卓要做出相应处理,比如语音免唤醒词的反注册)
  // _fetchQueue,native调用,获取消息data的方法
  // _handleMessageFromNative,native调用,发送消息给js端
  var la = window.la = {
    listen: listen,
    registerHandler: registerHandler,
    registerSpeech: registerSpeech,
    _fetchQueue: _fetchQueue,
    _handleMessageFromNative: _handleMessageFromNative
  };
​
  ['showTitle', 'playTTS','navigateMap'].forEach(a => {
    la[a] = function (data, responseCallback) {
      callHandler(a, data, responseCallback)
    }
  })
​
  var doc = document
  _createQueueReadyIframe(doc)
  var readyEvent = doc.createEvent('Events')
  readyEvent.initEvent('WebViewJavascriptBridgeReady')
  readyEvent.bridge = la
  doc.dispatchEvent(readyEvent)
  init()
})()

压缩后的js文件

代码语言:javascript
复制
!function(e){var n={};function t(r){if(n[r])return n[r].exports;var o=n[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,t),o.l=!0,o.exports}t.m=e,t.c=n,t.d=function(e,n,r){t.o(e,n)||Object.defineProperty(e,n,{enumerable:!0,get:r})},t.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},t.t=function(e,n){if(1&n&&(e=t(e)),8&n)return e;if(4&n&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(t.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&n&&"string"!=typeof e)for(var o in e)t.d(r,o,function(n){return e[n]}.bind(null,o));return r},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},t.p="",t(t.s=0)}([function(e,n,t){e.exports=t(1)},function(e,n){!function(){if(!window.la){var e,n=[],t=[],r={},o={},a=1,i=window.la={listen:function(e,n){u(e),r[e]=n},registerHandler:function(e,n){r[e]=n},registerSpeech:function(e,n){u("_registerSpeech",e),e.forEach(e=>{r["_speech"+e]=n})},_fetchQueue:function(){var t=JSON.stringify(n);n=[],e.src="yy://return/_fetchQueue/"+encodeURIComponent(t)},_handleMessageFromNative:function(e){console.log(e),t&&t.push(e),f(e)}};["showTitle","playTTS","navigateMap","login"].forEach(e=>{i[e]=function(n,t){u(e,n,t)}});var c=document;!function(n){(e=n.createElement("iframe")).style.display="none",n.documentElement.appendChild(e)}(c);var l=c.createEvent("Events");l.initEvent("WebViewJavascriptBridgeReady"),l.bridge=i,c.dispatchEvent(l),function(e){if(i._messageHandler)throw new Error("WebViewJavascriptBridge.init called twice");i._messageHandler=e;var n=t;t=null;for(var r=0;r<n.length;r++)f(n[r]);console.log("la 1.0.0 inited")}()}function u(e,n,t){s({handlerName:e,data:n},t)}function s(t,r){if(r){var i="cb_"+a+++"_"+(new Date).getTime();o[i]=r,t.callbackId=i}n.push(t),e.src="yy://__QUEUE_MESSAGE__/"}function f(e){setTimeout((function(){var n,t=JSON.parse(e);if(t.responseId){if(!(n=o[t.responseId]))return;n(t.responseData),delete o[t.responseId]}else{if(t.callbackId){var a=t.callbackId;n=function(e){s({responseId:a,responseData:e})}}var c=i._messageHandler;t.handlerName&&(c=r[t.handlerName]);try{c(t.data,n)}catch(e){"undefined"!=typeof console&&console.log("WebViewJavascriptBridge: WARNING: javascript handler threw.",t,e)}}}))}}()}]);

然后我们进入正题来详细说明一下jsbridge的用法 首先是安卓端

代码语言:javascript
复制
JS端
​
首先注册安卓端注入的js文件的对象

document.addEventListener( 'WebViewJavascriptBridgeReady' , function() { console.log('la is ready') }, false );

代码语言:javascript
复制
然后就是约定的相关方法调用 js调用安卓本地注册的方法 传递网页的数据给到安卓端使用
下面是获取经纬度坐标来传递给安卓端 安卓端去调用导航来实现功能的js端代码

la.navigateMap({ latitude: detailData.lat +'', // gcj02坐标 longitude: detailData.lon +'', // gcj02坐标 address: detailData.address +'' // 地址 // 地址 },function (A2JData) { console.log("form Android to JS:" + A2JData); })

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系转载前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
作者已关闭评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档