#### 简易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");
}
}];
}];
}];
}
}
}
```