美文网首页
简易jsbridge实现

简易jsbridge实现

作者: jinbo007 | 来源:发表于2021-04-07 14:02 被阅读0次

#### 简易JsBridge设计

#### 1.初衷

解决请求范式

(仿微信):

```

wx.getLocation({

  type: 'wgs84', // 默认为wgs84的gps坐标

  success: function (res) {

    var latitude = res.latitude; // 纬度,浮点数,范围为90 ~ -90

    var longitude = res.longitude; // 经度,浮点数,范围为180 ~ -180。

  }

});

```

实现目标请求方式

```

jlpay.getLocation({

  'type':'gps084',

  succ:function (res) {

  }

});

```

直接试下,走起

> 尝试1 , 直接尝试接收JsonObject,报错

```

webView.addJavascriptInterface(new JsInterace(), "jsbridge");

```

```

@JavascriptInterface

public void getLocation(String content) {

    showMsg(" 客户端收到消息:" + content);

}

```

```

window.jsbridge.getLocation({'type':'gps084',succ:function(res){}});

客户端收到消息: undefined

```

> 尝试2, 参数分开调用,原生收到的消息 客户端收到消息:undefined

```

@JavascriptInterface

public void getLocation(String method,String callback) {

  showMsg(" 客户端收到消息:" + method+" + "+callback);

}

```

```

window.jlpay.getLocation("wgs84",function(res){});

收到结果: 原生收到的消息 客户端收到消息:wgs84 + undefined

```

#### 2.设计思路

##### 2.1基础参考

###### 2.1.1. web端调用原生代码方法

###### 1) js调用

webView注入 js对象

```

webView.addJavascriptInterface(new JsInterace(), "jsbridge")

public class JsInterace {       

  @JavascriptInterface

  public void send(String msg) {}

}

```

web 端调用 

```

window.jsbridge.send("hello");

```

###### 2)scheme方式调用

web端scheme调用

```

document

.getElementById('hello')

.addEventListener('click', function () {

  window.location.href = 'jlmerchant://hello?

  jsCallBack=hello&text=helloWorld'

})

```

原生在webView接收

```

@Override

public boolean shouldOverrideUrlLoading(WebView view, String url) {

    return super.shouldOverrideUrlLoading(view, url);   

}

```

###### 2.1.2 原生调用web端方法

方法一 loadUrl

```

String js_content = "jlpay.receive('客户端收到了')"

webView.loadUrl("javascript:"+js_content)

```

方法二 evaluateJavascript(api 19+)

```

webView.evaluateJavascript(js_content,null);

```

###### 2.3.设计思路(jsbridge)

问题:无法接收 JsonObject 或 Function对象

方案:设计 jsbridge 中转

5步走

>**web调用--> jsbridge转发 -->原生处理-->jsbridge接收-->回调web端**

第1步.web调用。

```

jlpay.getLocation({

  'type':'gps084',

  success:function (res) {}

});

```

第2步.jsbridge转发

```

//发送消息的通道, method(方法名),message(消息体)

  function getLocation(data){

    //取到回调对象

    var success = data.success;

    //为当前callback生成一个callbackId

    callbackId = 'cb_id_' + (callbackUnionId++);

    //保存当前方法的callback对象

    responseCallbacks[callbackId]=success;

    //调用原生方法

    jlpay.getLocation(JSON.stringify(data),callbackId);

  }

```

第3步. java处理及回调。

```

    @JavascriptInterface

    public void getLocation(String msg, String callbackId) {

        String js_content =

            String.format("javascript:jsbridge.receive('%s')"

            ,locationData());

        //回调回jsbridge

        webView.loadUrl(js_content);

    }

    //模拟定位信息

    private void locationData(String callbackId) {

      JSONObject object = new JSONObject();

        try {

            object.put("callbackId", callbackId);

            JSONObject location = new JSONObject();

            location("longitude", 35.24324324);

            location("latitude", 135.24324324);

            object.put("data", location);

        } catch (JSONException e) {

            e.printStackTrace();

        }

        retrun object.toString();

    }

```

第4步. jsbridge.js接收并回调web端

```   //用于接收来自客户端的信息

    function receive(messageJson){

      var message =  JSON.parse(messageJson);

      var callbackId = message.callbackId;

      var data = message.data;

    }

```

第5步. 回调web端

```

//找到存储的callback 并回调

responseCallbacks[callbackId](data);

```

#### 3. one more thing (扩展方案)

嘉联支付App-scheme方案

1.web 端调用

``` 

//定义接收方法

window.hello = function (res) { //处理原生实现的方法}

//调用

document.getElementById('hello').addEventListener('click',

    function () {

    window.location.href =

    'jlmerchant://getLocation? jsCallBack=hello&text=helloWorld'

})

```

2.原生解析并回调

```

public boolean shouldOverrideUrlLoading(WebView view, String url) {

    //解析scheme接收参数信息

    if (url.startsWith("jlmerchant://")) {

        handleData(url);

        return true;

    }

}

//处理并回调

//jlmerchant://getLocation?jsCallBack=hello

handleData(String url){ 

    Uri uri = Uri.parse(path);

    String host = uri.getAuthority();

    String fuction = uri.getQueryParameter("jsCallBack");

    switch (host) {

    case "getLocation":

        JSONObject locationData=getSuccResponse(context);

        try{               

            locationData.put("longitude","35.48484838");

            locationData.put("latitude","113.83848484");

        } catch (JSONException e) {

            e.printStackTrace();

        }

    、、回调

    webView.loadUrl("javascript:"+fuction+"('" +locationData.toString() + "')");

    break;

}

``` 

##### 思考:

###### Q1-调试及异常处理

```

function send(data,callback){

  log('jljsbridge 发送数据 :'+data+ 'callback:'+callback);

  var callbackId = 'cb_id_' + (callbackUnionId++);

  log('jljsbridge 的callbackId是:'+callbackId);

  responseCallbacks[callbackId]=callback;

  log('jsBridge 回复消息 完成');

  try{

        window.jlpay.send( JSON.stringify(data),callbackId);

        log('jljsbridge 调用成功');

  }catch(e){

        console.log('jljsbridge 加载错误'+e);

  }

}

```

###### Q2-方案对比

  jsbridge方案 or  scheme方案

###### Q3-双向调用

```

  webView.evaluateJavascript(js_content,new ValueCallback<String>(){

        @Override

        public void onReceiveValue(String value) {

        //接收js返回结果

        }

    });

```

##### Tips:

备注问题:

1.Java端加载js方法,主线程调用,否则

![](https://img.haomeiwen.com/i7774268/5e34d44bc13240d5.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

2.注入的时机 //onPageFinished

3.格式化Json

```

/**

    * 去掉json的特殊字符

    *

    * @param messageJson

    * @return

    */

    public static String formatJson(String messageJson) {

        messageJson = messageJson.replaceAll("(\\\\)([^utrn])", "\\\\\\\\$1$2");

        messageJson = messageJson.replaceAll("(?<=[^\\\\])(\")", "\\\\\"");

        messageJson = messageJson.replaceAll("(?<=[^\\\\])(\')", "\\\\\'");

        messageJson = messageJson.replaceAll("%7B", URLEncoder.encode("%7B"));

        messageJson = messageJson.replaceAll("%7D", URLEncoder.encode("%7D"));

        messageJson = messageJson.replaceAll("%22", URLEncoder.encode("%22"));

        return messageJson;

    }

```

##### 参考:

jsbridge:https://github.com/lzyzsd/JsBridge

本文demo示例: https://github.com/jinbo1232007/Jsbridge

##### 附iOS端实现参考:

iOS WKWebView和 h5交互

##### 1.加入js中间处理,将js方法转换成window.webkit.messageHandlers方法发送给app

```

javascript

window.jlpay = new Object();

var jlpaySuccessObject = {};

var id = 0;

window.jlpay.getLocation = function(object){

    id++;

    try {

        jlpaySuccessObject[id] = object.success;

        var jlpayCallBack = function(res){

            var tempId = res.jlpayId;

            delete res.jlpayId;

            (jlpaySuccessObject[id])(res);

            jlpaySuccessObject[tempId]=null;

        }

        window.webkit.messageHandlers.jlGetLocation.postMessage({"type":object.type,

        "callBack":jlpayCallBack.toString(),

        "jlpayId":id});

    } catch(e) {

        alert(e.message)

    }

}

```

##### 2.WKWebView 加载中间js文件,并监听jsGetLocation方法

```objective-c

WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];

//加载中间js文件

[config.userContentController addUserScript:[self userScript]];

//监听jsGetLocation方法

[config.userContentController addScriptMessageHandler:self name:@"jsGetLocation"];

```

##### 3.jsGetLocaiton 回调响应处理,并将处理结果回传给h5

```objective-c

#pragma mark - WKScriptMessageHandler

- (void)userContentController:(nonnull WKUserContentController *)userContentController didReceiveScriptMessage:(nonnull WKScriptMessage *)message {

    NSString *messageName = message.name;

    if ([messageName isEqualToString:@"jsGetLocation"]) {

        if ([message.body isKindOfClass:[NSDictionary class]]) {

            NSDictionary *bodyDict = (NSDictionary *)message.body;

            NSString *type    = bodyDict[@"type"];

            NSString *timeout  = bodyDict[@"timeout"];

            NSString *callBack = bodyDict[@"callBack"];

            NSString *jlpayId  = bodyDict[@"jlpayId"];

            __weak typeof(self)weakSelf = self;

            [self getLocation:type timeout:timeout jlpayId:jlpayId callBlk:^(NSString *jsonStr) {

                __strong typeof(self)strongSelf = weakSelf;

                [strongSelf callBackStringWithJsonString:jsonStr backFuncStr:callBack callBlk:^(NSString *backFuncString) {

                    //操作完成回调结果给h5

                    [message.webView evaluateJavaScript:backFuncString completionHandler:^(id _Nullable obj, NSError * _Nullable error) {

                        if (error) {

                            JLLogE(@"webview", @"error:%@",error.localizedDescription);

                        }

                        else{

                            JLLogI(@"webview", @"jl callback success");

                        }

                    }];

                }];

            }];

        }

    }

}

```

相关文章

网友评论

      本文标题:简易jsbridge实现

      本文链接:https://www.haomeiwen.com/subject/qikgxftx.html