簡易jsbridge實現(xiàn)

#### 簡易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");

? ? ? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? ? ? }];

? ? ? ? ? ? ? ? }];

? ? ? ? ? ? }];

? ? ? ? }

? ? }

}

```

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末钓株,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子陌僵,更是在濱河造成了極大的恐慌轴合,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,348評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件碗短,死亡現(xiàn)場離奇詭異受葛,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)偎谁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,122評論 2 385
  • 文/潘曉璐 我一進(jìn)店門总滩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人搭盾,你說我怎么就攤上這事咳秉。” “怎么了鸯隅?”我有些...
    開封第一講書人閱讀 156,936評論 0 347
  • 文/不壞的土叔 我叫張陵澜建,是天一觀的道長向挖。 經(jīng)常有香客問我,道長炕舵,這世上最難降的妖魔是什么何之? 我笑而不...
    開封第一講書人閱讀 56,427評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮咽筋,結(jié)果婚禮上溶推,老公的妹妹穿的比我還像新娘。我一直安慰自己奸攻,他們只是感情好蒜危,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,467評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著睹耐,像睡著了一般辐赞。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上硝训,一...
    開封第一講書人閱讀 49,785評論 1 290
  • 那天响委,我揣著相機(jī)與錄音,去河邊找鬼窖梁。 笑死赘风,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的纵刘。 我是一名探鬼主播邀窃,決...
    沈念sama閱讀 38,931評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼彰导!你這毒婦竟也來了蛔翅?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,696評論 0 266
  • 序言:老撾萬榮一對情侶失蹤位谋,失蹤者是張志新(化名)和其女友劉穎山析,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體掏父,經(jīng)...
    沈念sama閱讀 44,141評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡笋轨,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,483評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了赊淑。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片忿晕。...
    茶點(diǎn)故事閱讀 38,625評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡薇芝,死狀恐怖焕阿,靈堂內(nèi)的尸體忽然破棺而出舔株,到底是詐尸還是另有隱情,我是刑警寧澤饱岸,帶...
    沈念sama閱讀 34,291評論 4 329
  • 正文 年R本政府宣布掺出,位于F島的核電站徽千,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏汤锨。R本人自食惡果不足惜双抽,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,892評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望闲礼。 院中可真熱鬧牍汹,春花似錦、人聲如沸柬泽。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽锨并。三九已至钧嘶,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間琳疏,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工闸拿, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留空盼,地道東北人。 一個月前我還...
    沈念sama閱讀 46,324評論 2 360
  • 正文 我出身青樓新荤,卻偏偏與公主長得像揽趾,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子苛骨,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,492評論 2 348

推薦閱讀更多精彩內(nèi)容