Skip to content
On this page

Web 与原生应用的常见通信方式

前言

在移动web开发过程中,一定会遇到web端与APP端交互的情况。下面就来分析下,web端到底是如何与APP端实现交互的。

APP端,iOSAndriod的实现方式各不一样。

大家都知道,我们的javascript大部分是运行在浏览器上的,这时浏览器的环境就是宿主环境(host environment)则给我们的javascript提供了window,navigator等宿主对象。

Andriod

Android上,实现的方式就和上面这种方式类似,就是在webview上注册一个全局变量,然后我们再js直接调用即可。下面即表示在全局环境下定义了一个对象androidEnv :

java
webview.addJavascriptInterface(object, "androidEnv");

web调用Andriod

Andriodwebview这个类里面定义的方法,我们在js都可以直接调用,这样就实现了webAndriod的单向交互了,例如:

js
AndriodEnv.sayHi(); //注意:sayHi是Andriod定义的方法

Andriod调用web

然后,如果我们要实现Andriodweb的单向交互呢,道理也一样。即是js定义一个全局的函数,然后Andriod就可以直接调用了:

js
function globalFunc(){
  return 'hello Andriod';
}

iOS

相反在iOS上,则没这么方便了,需要用到一个叫WebViewJavascriptBridge的桥接中间件。

js上我们需要判断这个bridge是否已经注册,如果有就直接拿来使用;否则就监听一下brideg的变化,然后再使用。因此我们先封装好一个方法:

js
function connectWebViewJavascriptBridge(callback) {
    if (window.WebViewJavascriptBridge) {
      callback(WebViewJavascriptBridge);
    }
    else {
      document.addEventListener('WebViewJavascriptBridgeReady', function() {
        callback(WebViewJavascriptBridge);
      }, false);
    };
  }

之后我们用一个callback来处理这个bridge

js
connectWebViewJavascriptBridge(function(bridge) {

    bridge.init(function(data, responseCallback) {});
    
});

到这个时候,基础工作就做完了,接下来就是webiOS之间的交互了。

web调用iOS

js
bridge.callHandler('ObjC Echo', {'key':'value'}, function responseCallback(responseData) {
    console.log("JS received response:", responseData)
})

其中,ObjC EchoiOS上的对应方法(其实并不是函数,只是一个token),{'key':'value'}则是web传到iOS的参数,responseData则是iOS回传的数据。

这里还有另外一种方法,就是我们可以直接利用bridgesend方法,直接将所有东西都send过去给iOS即可,例如:

js
var param = {
  "functionName" : "share",
  "params":{
    "title" : opt.title,
    "desc" : opt.desc
  }
};

connectWebViewJavascriptBridge(function(bridge) {

  bridge.send(param);
});

send可以接受两个参数,第二个参数就是回调函数了:

js
connectWebViewJavascriptBridge(function(bridge) {

  bridge.send(param, function(data){
    callback(data);
  });

});

iOS调用web

js
bridge.registerHandler('JS Echo', function(data, responseCallback) {
    console.log("JS Echo called with:", data)
    responseCallback(data)
})

这里的JS Echo则是JS上注册的一个方法名,当iOS执行玩这个方法,我们就可以马上监听到并接受到一些数据data,之后我们还可以执行responseCallback回调iOS告诉它我们的处理情况。

说说js代码

知道如何实现了之后,就涉及到软件工程的问题了。要如何编写代码结构,最优化实现我们的需求才是重中之重。首先先贴上一份代码,大家可以先考虑如何优化再往下看。

js
jAPP = {
  getUserName: function(){
    if(inAPP) {
      if(iOS){
        //...
      }
      if(Andriod){
        //...
      }
    }
    else {
      //...
      console.log('请在APP内打开')
    }
  },

  getUserId: function(){
    if(inAPP) {
      if(iOS){
        //...
      }
      if(Andriod){
        //...
      }
    }
    else {
      //...
      console.log('请在APP内打开')
    }
  }
}

我认为的缺点

  • 重复判断设备信息,因为设备信息可以理解成常量,一经获取就不会更改,所以我们可以用初始化分支来优化。

  • 重复判断是否在APP内。同样可以采用初始化分支来优化,也就是说不在APP内的话,调用的方法都不需要初始化了。

  • console.log没有意义,只有在测试环境下能有,上生产应该去除。

优化

js
var ua = navigator.userAgent().toLowerCase();
var inIOS = !!( ua.indexOf('iphone') > -1 || ua.indexOf('ipad') > -1 );
var inAndriod = !!( ua.indexOf('andriod') > -1 );
var inAPP = !!( ua.indexOf('your APP token') > -1 );
var jAPP = {};

if(inAPP) {
  
  if(inIOS) {
    jAPP.getUserId = function(){
      //..
    };
    jAPP.getUserName = function(){
      //..
    };
  }

  else if(inAndriod) {

    jAPP.getUserId = function(){
      //..
    };

    jAPP.getUserName = function(){
      //..
    };
  }
}

初始化分支的意思就是说在js脚本开始执行的的时候就确定好分支,而不是每次执行的时候才去确定分支。好处在于不用重复判断分支。

如果你有更好的做法,希望你可以在下方给大家分享一下。谢谢。

参考

JS与WebView交互存在的一些问题

WebViewJavascriptBridge