#### 簡易JsBridge設(shè)計
#### 1.初衷
解決請求范式
(仿微信):
```
wx.getLocation({
? type: 'wgs84', // 默認(rèn)為wgs84的gps坐標(biāo)
? success: function (res) {
? ? var latitude = res.latitude; // 緯度,浮點(diǎn)數(shù)凄杯,范圍為90 ~ -90
? ? var longitude = res.longitude; // 經(jīng)度,浮點(diǎn)數(shù)秉宿,范圍為180 ~ -180戒突。
? }
});
```
實現(xiàn)目標(biāo)請求方式
```
jlpay.getLocation({
? 'type':'gps084',
? succ:function (res) {
? }
});
```
直接試下,走起
> 嘗試1 , 直接嘗試接收J(rèn)sonObject描睦,報錯
```
webView.addJavascriptInterface(new JsInterace(), "jsbridge");
```
```
@JavascriptInterface
public void getLocation(String content) {
? ? showMsg(" 客戶端收到消息:" + content);
}
```
```
window.jsbridge.getLocation({'type':'gps084',succ:function(res){}});
客戶端收到消息: undefined
```
> 嘗試2, 參數(shù)分開調(diào)用膊存,原生收到的消息 客戶端收到消息:undefined
```
@JavascriptInterface
public void getLocation(String method,String callback) {
? showMsg(" 客戶端收到消息:" + method+" + "+callback);
}
```
```
window.jlpay.getLocation("wgs84",function(res){});
收到結(jié)果: 原生收到的消息 客戶端收到消息:wgs84 + undefined
```
#### 2.設(shè)計思路
##### 2.1基礎(chǔ)參考
###### 2.1.1. web端調(diào)用原生代碼方法
###### 1) js調(diào)用
webView注入 js對象
```
webView.addJavascriptInterface(new JsInterace(), "jsbridge")
public class JsInterace {? ? ? ?
? @JavascriptInterface
? public void send(String msg) {}
}
```
web 端調(diào)用?
```
window.jsbridge.send("hello");
```
###### 2)scheme方式調(diào)用
web端scheme調(diào)用
```
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 原生調(diào)用web端方法
方法一 loadUrl
```
String js_content = "jlpay.receive('客戶端收到了')"
webView.loadUrl("javascript:"+js_content)
```
方法二 evaluateJavascript(api 19+)
```
webView.evaluateJavascript(js_content,null);
```
###### 2.3.設(shè)計思路(jsbridge)
問題:無法接收 JsonObject 或 Function對象
方案:設(shè)計 jsbridge 中轉(zhuǎn)
5步走
>**web調(diào)用--> jsbridge轉(zhuǎn)發(fā) -->原生處理-->jsbridge接收-->回調(diào)web端**
第1步.web調(diào)用。
```
jlpay.getLocation({
? 'type':'gps084',
? success:function (res) {}
});
```
第2步.jsbridge轉(zhuǎn)發(fā)
```
//發(fā)送消息的通道, method(方法名),message(消息體)
? function getLocation(data){
? ? //取到回調(diào)對象
? ? var success = data.success;
? ? //為當(dāng)前callback生成一個callbackId
? ? callbackId = 'cb_id_' + (callbackUnionId++);
? ? //保存當(dāng)前方法的callback對象
? ? responseCallbacks[callbackId]=success;
? ? //調(diào)用原生方法
? ? jlpay.getLocation(JSON.stringify(data),callbackId);
? }
```
第3步. java處理及回調(diào)忱叭。
```
? ? @JavascriptInterface
? ? public void getLocation(String msg, String callbackId) {
? ? ? ? String js_content =
? ? ? ? ? ? String.format("javascript:jsbridge.receive('%s')"
? ? ? ? ? ? ,locationData());
? ? ? ? //回調(diào)回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接收并回調(diào)web端
``` ? //用于接收來自客戶端的信息
? ? function receive(messageJson){
? ? ? var message =? JSON.parse(messageJson);
? ? ? var callbackId = message.callbackId;
? ? ? var data = message.data;
? ? }
```
第5步. 回調(diào)web端
```
//找到存儲的callback 并回調(diào)
responseCallbacks[callbackId](data);
```
#### 3. one more thing (擴(kuò)展方案)
嘉聯(lián)支付App-scheme方案
1.web 端調(diào)用
```?
//定義接收方法
window.hello = function (res) { //處理原生實現(xiàn)的方法}
//調(diào)用
document.getElementById('hello').addEventListener('click',
? ? function () {
? ? window.location.href =
? ? 'jlmerchant://getLocation? jsCallBack=hello&text=helloWorld'
})
```
2.原生解析并回調(diào)
```
public boolean shouldOverrideUrlLoading(WebView view, String url) {
? ? //解析scheme接收參數(shù)信息
? ? if (url.startsWith("jlmerchant://")) {
? ? ? ? handleData(url);
? ? ? ? return true;
? ? }
}
//處理并回調(diào)
//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();
? ? ? ? }
? ? 隔崎、、回調(diào)
? ? webView.loadUrl("javascript:"+fuction+"('" +locationData.toString() + "')");
? ? break;
}
```?
##### 思考:
###### Q1-調(diào)試及異常處理
```
function send(data,callback){
? log('jljsbridge 發(fā)送數(shù)據(jù) :'+data+ 'callback:'+callback);
? var callbackId = 'cb_id_' + (callbackUnionId++);
? log('jljsbridge 的callbackId是:'+callbackId);
? responseCallbacks[callbackId]=callback;
? log('jsBridge 回復(fù)消息 完成');
? try{
? ? ? ? window.jlpay.send( JSON.stringify(data),callbackId);
? ? ? ? log('jljsbridge 調(diào)用成功');
? }catch(e){
? ? ? ? console.log('jljsbridge 加載錯誤'+e);
? }
}
```
###### Q2-方案對比
? jsbridge方案 or? scheme方案
###### Q3-雙向調(diào)用
```
? webView.evaluateJavascript(js_content,new ValueCallback<String>(){
? ? ? ? @Override
? ? ? ? public void onReceiveValue(String value) {
? ? ? ? //接收js返回結(jié)果
? ? ? ? }
? ? });
```
##### Tips:
備注問題:
1.Java端加載js方法,主線程調(diào)用韵丑,否則
![](https://upload-images.jianshu.io/upload_images/7774268-5e34d44bc13240d5.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
2.注入的時機(jī) //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端實現(xiàn)參考:
iOS WKWebView和 h5交互
##### 1.加入js中間處理爵卒,將js方法轉(zhuǎn)換成window.webkit.messageHandlers方法發(fā)送給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文件,并監(jiān)聽jsGetLocation方法
```objective-c
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
//加載中間js文件
[config.userContentController addUserScript:[self userScript]];
//監(jiān)聽jsGetLocation方法
[config.userContentController addScriptMessageHandler:self name:@"jsGetLocation"];
```
##### 3.jsGetLocaiton 回調(diào)響應(yīng)處理撵彻,并將處理結(jié)果回傳給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) {
? ? ? ? ? ? ? ? ? ? //操作完成回調(diào)結(jié)果給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");
? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? }];
? ? ? ? ? ? ? ? }];
? ? ? ? ? ? }];
? ? ? ? }
? ? }
}
```