前言:

因为现在对移动端动态化的需求越来越大,在许多APP中一般都会使用webview控件去嵌入一些web页面,从而去避免频繁的发版问题。但有时web页面是需要去调用原生的一些功能,这时候就需要JavaScript与Native之间去进行一些交互。

交互方式:

一、通过schema方式交互。

二、客户端向webview注入变量供JavaScript调用。

三、通过桥接方式交互。

方式1:schema方式交互

这种方式一般用于较为简单的交互行为(不需要返回值),比如在web页面上点击一个按钮去调用客户端的回退页面操作,那么步骤大概如下:

1.前端与客户端约定好这个schema名称(现在与iOS约定的名称是objc)。

2.前端通过window.location.href = “objc://goback” 的方式去发起请求。

3.客户端拦截objc这个schema,然后去解析要执行的方法具体是什么(这里是goback),然后执行相应的操作。

4.如果需要向客户端传递参数在后边直接拼接即可,如: “objc://goback:/参数1:/参数2″,客户端解析即可。注意:如果参数是字符串,务必使用encodeURIComponent先进行编码;参数也可以是一个JS全局函数,客户端执行完原生方法后去调用这个全局函数,也可实现回调功能,但这种方式不太优雅。

方式2:向webview注入变量

这种方式就是客户端将原生方法直接注入到当前webview中,可以直接通过JavaScript调用,对前端来说较为方便。

例如:

 客户端向我们的web页面中注入了这样一个对象:

window.test = {
   hello:function(){
     console.log('hello test')
   }
}

我们直接在页面中调用即可:

if(window.test){
  window.test.hello();
}

方式三:通过桥接方式交互

当web页面与客户端交互较为复杂时,需要各种回调参数,以及两端之间的相互调用;这种情况我们可以通过引入第三方库来实现,当然也需要客户端引入相应的包。

Android:JsBridge

iOS:WebViewJavascriptBridge

引入第三方库:

在web页面中引入这两个库,并根据设备系统去加载对应的JS,如下:

//获取浏览器版本
function Versions() {
  var u = navigator.userAgent,
    app = navigator.appVersion;
  return {
    trident: u.indexOf('Trident') > -1, //IE内核                
    presto: u.indexOf('Presto') > -1, //opera内核                
    webKit: u.indexOf('AppleWebKit') > -1, //苹果、谷歌内核                
    gecko: u.indexOf('Gecko') > -1 && u.indexOf('KHTML') == -1, //火狐内核                
    mobile: !!u.match(/AppleWebKit.*Mobile.*/) || !!u.match(/AppleWebKit/), //是否为移动终端                
    ios: !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/), //ios终端                
    android: u.indexOf('Android') > -1 || u.indexOf('Linux') > -1, //android终端或者uc浏览器                
    iPhone: u.indexOf('iPhone') > -1 || u.indexOf('Mac') > -1, //是否为iPhone或者QQHD浏览器                
    iPad: u.indexOf('iPad') > -1, //是否iPad                
    webApp: u.indexOf('Safari') == -1, //是否web应该程序,没有头部与底部      
    weiXin: u.indexOf('MicroMessenger') > -1 //是否是微信
  };
}
let devices = '';
//根据设备类型执行相应的桥接代码
if (Versions().mobile && Versions().android) {
  devices = "android"
    ; (function () {
      if (window.WebViewJavascriptBridge) { return }

      var messageHandlers = {}
      var responseCallbacks = {}
      var uniqueId = 1

      function init(messageHandler) {
        if (WebViewJavascriptBridge._messageHandler) { throw new Error('WebViewJavascriptBridge.init called twice') }
        WebViewJavascriptBridge._messageHandler = messageHandler
      }

      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)
      }

      function _doSend(message, responseCallback) {
        console.log("responseCallback:" + responseCallback);
        if (responseCallback) {
          var callbackId = 'cb_' + (uniqueId++) + '_' + new Date().getTime()
          responseCallbacks[callbackId] = responseCallback
          message['callbackId'] = callbackId
        }
        console.log("sending:" + JSON.stringify(message));
        if (typeof _WebViewJavascriptBridge == "undefined") {
          return;
        }
        _WebViewJavascriptBridge._handleMessageFromJs((typeof message.data == 'string') && message.data.constructor == String ? message.data : JSON.stringify(message.data) || null, message.responseId || null,
          message.responseData || null, message.callbackId || null, message.handlerName || null);

      }

      function _dispatchMessageFromJava(messageJSON) {
        var message = JSON.parse(messageJSON)
        var messageHandler

        if (message.responseId) {
          var responseCallback = responseCallbacks[message.responseId]
          if (!responseCallback) { return; }
          responseCallback((typeof message.responseData == 'string') && message.responseData.constructor == String ? message.responseData : JSON.parse(message.responseData))
          delete responseCallbacks[message.responseId]
        } else {
          var responseCallback
          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]
          }
          try {
            handler(message.data, responseCallback)
          } catch (exception) {
            if (typeof console != 'undefined') {
              console.log("WebViewJavascriptBridge: WARNING: javascript handler threw.", message, exception)
            }
          }
        }
      }


      function _handleMessageFromJava(messageJSON) {
        _dispatchMessageFromJava(messageJSON)
      }

      //export
      window.WebViewJavascriptBridge = {
        init: init,
        send: send,
        registerHandler: registerHandler,
        callHandler: callHandler,
        _handleMessageFromJava: _handleMessageFromJava
      }

      //dispatch event
      var doc = document;
      var readyEvent = doc.createEvent('Events');
      readyEvent.initEvent('WebViewJavascriptBridgeReady');
      readyEvent.bridge = WebViewJavascriptBridge;
      doc.dispatchEvent(readyEvent);
    })();
} else {
  (function () {
    window.setupWebViewJavascriptBridge = function (callback) {
      if (window.WebViewJavascriptBridge) { return callback(WebViewJavascriptBridge); }
      if (window.WVJBCallbacks) { return window.WVJBCallbacks.push(callback); }
      window.WVJBCallbacks = [callback];
      var WVJBIframe = document.createElement('iframe');
      WVJBIframe.style.display = 'none';
      WVJBIframe.src = 'https://__bridge_loaded__';
      document.documentElement.appendChild(WVJBIframe);
      setTimeout(function () { document.documentElement.removeChild(WVJBIframe) }, 0)
    }

  })()
  devices = "ios"
}

封装调用方法

JS调用客户端方法

/**
 * @method js调用原生客户端方法
 * @param {String} method 方法名 
 * @param {Object} params 参数对象 
 * @param {Function} call 回调函数
 */
let JsCallNative = function (method,params, call) {
  if (devices == "ios") {
    window.setupWebViewJavascriptBridge(function (bridge) {
      bridge.callHandler(method , params ,
        function (response) {
          if (typeof call == "function") {
            call(response);
          }
        })
    });
  } else {
    window.WebViewJavascriptBridge.callHandler(method , params ,
      function (response) {
        if (typeof call == "function") {
          call(JSON.parse(response));
        }
      })
  }

};

参数说明:

 1.method:两端约定好的方法名

 2.params:需要传递给客户端的参数

 3.call : 回调参数,如果需要回调值或者回调操作即可传入

客户端调用JS方法

/**
 * @method 原生客户端调用Js方法
 * @param {String} method 方法名 
 * @param {Function} call  回调函数 
 */
let NativeCallJs = function (method, call) {
  if (devices == "ios") {
    window.setupWebViewJavascriptBridge(function (bridge) {
      bridge.registerHandler(method, function (data, responseCallback) {
        var responseData = { 'Javascript Says': 'Right back atcha!' }
        responseCallback(responseData);
        if (typeof call == "function") {
          responseData = call(data);
        }
        responseCallback(responseData)
      })
    });
  } else {
    window.WebViewJavascriptBridge.registerHandler(method, function (data, responseCallback) {
      var responseData = { 'Javascript Says': 'Right back atcha!' }
      if (typeof call == "function") {
        responseData = call(JSON.parse(data));
      }
      responseCallback(responseData)
    });
  }

};

参数说明:

   1.method 约定的方法名

   2.call 回调函数

其他

DSBridge 是一个三端易用现代跨平台JavaScript bridge,可实现JavaScript与原生之间同步或异步的调用彼此的函数,能够保持前端一套代码在两端运行。

相关链接:

DSBridge-Android

DSBridge-IOS

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注