简易jsbridge实现

#### 简易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://upload-images.jianshu.io/upload_images/7774268-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");

                        }

                    }];

                }];

            }];

        }

    }

}

```

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,544评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,430评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,764评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,193评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,216评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,182评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,063评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,917评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,329评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,543评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,722评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,425评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,019评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,671评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,825评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,729评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,614评论 2 353

推荐阅读更多精彩内容