web与Native通信之回调函数处理

264 阅读2分钟

上一篇文章介绍了URL Scheme拦截和JSBridge注入,这篇文章主要介绍如何在使用上述方式通信时获得回调。

以下示例通过JSBridge的方式来演示双端通信回调函数的使用。Scheme方式与其相同,只是将传递到JSBridge中的参数拼接到URL Scheme的参数中去,在Native端通过截取到对应方法后的参数,来做相应的回调。

回调逻辑介绍

以Android端调用Native输入框为例,将Native端输入的message通过回调函数传递回Android端。

  1. web调用Native输入框
  2. 用户在Native输入框输入message
  3. Native端拿到用户输入的message传递给web端
  4. web端接收到message执行回调函数

因此在整个流程中要明确以下两个点:

  1. 回调函数通信了两次,第一次是web携带回调函数名称调用Native,第二次是Native携带返回值调用web;
  2. 回调函数位于web端,是用来处理Native端返回的message

image.png

web端

// 调用Native输入框函数
getMessage() {
  // 调用前先将回调函数挂载到window上
  window.setMessage = message => console.log('--setMessage--', message);
  // 调用JSBridge对应方法
  NativeBridge. getNativeEditTextValue("setMessage");
}

Andriod端

webView.addJavascriptInterface(new NativeBridge(this), "NativeBridge");

// 注入的JSBridge对象
class NativeBridge {
  private Context ctx;
  NativeBridge(Context ctx) {
    this.ctx = ctx;
  }

  @JavascriptInterface
  public void getNativeEditTextValue(int callbackName) {
    MainActivity mainActivity = (MainActivity)ctx;
    String value = mainActivity.editText.getText().toString();
    // 调用web上面的回调方法,并将value传递进去
    String jsCode = "window." + callbackName + "(" + value + ")");
    mainActivity.runOnUiThread(new Runnable() {
      @Override
      public void run() {
        mainActivity.webView.evaluateJavascript(jsCode, null);
      }
    });
  }
}

回调的问题

上面的方法是回调函数最简单的方式,但是有一个弊端就是在全局对象window上定义了回调函数名称。

很显然这样做可能造成与window上其他函数重名覆盖,污染全局变量的问题。

解决办法

解决办法就是将回调函数放入到一个专门处理回调的全局对象中,通过一个id或者名称去标识他。虽然在两端通信时仍然传递的是回调名称或id,但是在传递之前多了一个将回调保存到一个全局对象中的过程,在传递之后多了一个从全局对象中取出这个回调的过程。

web端

let callbackId = 0;
// 保存回调函数的全局对象
let callbackMap = {};

window.JSSDK = {
    // 将回调保存到一个全局对象中
    storeCallback: callback => {
        // 使用名称存储:setMessage 
        // callbackMap['setMessage'] = callback;
        // window.NativeBridge.getNativeEditTextValue("setMessage");

        // 使用id存储
        callbackId++;
        callbackMap[callbackId] = callback;
        window.NativeBridge.getNativeEditTextValue(callbackId);
    },
    // 从全局对象中取出这个回调
    takeCallback: (callbackName, message) => {
      if(callbackMap[callbackName]) {
        callbackMap[callbackName](message);
      } else {
          // 回调函数不存在的处理情况
      }
    }
}

Native端

@JavascriptInterface
public void getNativeEditTextValue(int callbackName) { 
    MainActivity mainActivity = (MainActivity)ctx;
    String value =mainActivity.editText.getText().toString(); 
    // 调用web上从全局对象中取出这个回调的方法,并将value传递进去 
    String jsCode = "window.JSSDK.takeCallback(%s, '%s')", callbackName, value);
    mainActivity.runOnUiThread(new Runnable() { 
        @Override public void run() { 
            mainActivity.webView.evaluateJavascript(jsCode, null); 
        } 
        }); 
}