1.JSBridge介紹
JSBridge 簡(jiǎn)單來(lái)講,主要是 給 JavaScript 提供調(diào)用 Native 功能的接口帕翻,讓混合開發(fā)中的『前端部分』可以方便地使用地址位置、攝像頭甚至支付等 Native 功能。
既然是『簡(jiǎn)單來(lái)講』淤堵,那么 JSBridge 的用途肯定不只『調(diào)用 Native 功能』這么簡(jiǎn)單寬泛。實(shí)際上顷扩,JSBridge 就像其名稱中的『Bridge』的意義一樣拐邪,是 Native 和非 Native 之間的橋梁,它的核心是 構(gòu)建 Native 和非 Native 間消息通信的通道隘截,而且是 雙向通信的通道扎阶。
所謂雙向通信的通道:
JS 向 Native 發(fā)送消息 : 調(diào)用相關(guān)功能、通知 Native 當(dāng)前 JS 的相關(guān)狀態(tài)等婶芭。
Native 向 JS 發(fā)送消息 : 回溯調(diào)用結(jié)果东臀、消息推送、通知 JS 當(dāng)前 Native 的狀態(tài)等雕擂。
1.1 JavaScript 調(diào)用 Native
JavaScript 調(diào)用 Native 的方式啡邑,主要有兩種:注入 API 和 攔截 URL SCHEME。
1.1.1注入 API
主要原理是井赌,通過(guò) WebView 提供的接口谤逼,向 JavaScript 的 window對(duì)象中注入對(duì)象或者方法瑰剃,讓 JavaScript 調(diào)用時(shí)弛随,直接執(zhí)行相應(yīng)的 Native 代碼邏輯,達(dá)到 JavaScript 調(diào)用 Native 的目的塘娶。
JavaScript可直接通過(guò)調(diào)用window對(duì)象下的對(duì)象或者方法纹坐,調(diào)用Native的相關(guān)接口枝冀。例如,舊版的掌通家園客戶端,通過(guò)全局注冊(cè)seebaby對(duì)象果漾,js可以這么調(diào)用客戶端:window.seebaby.getUserInfo()球切,
更多示例參考:3.4 客戶端與H5交互協(xié)議(o2o新增)
1.1.2攔截 URL SCHEME
先解釋一下 URL SCHEME:URL SCHEME是一種類似于url的鏈接,是為了方便app直接互相調(diào)用設(shè)計(jì)的绒障,形式和普通的 url 近似吨凑,例如:
ztjy://eyJjYWxsIjoibG9naW4iLCJhcmdzIjp7InR5cGUiOiIwIn19(格式:
發(fā)起這樣一個(gè)網(wǎng)絡(luò)請(qǐng)求有兩種方式:
1. 通過(guò)localtion.href;
2. 通過(guò)iframe方式户辱;
通過(guò)location.href有個(gè)問題鸵钝,就是如果我們連續(xù)多次修改window.location.href的值,在Native層只能接收到最后一次請(qǐng)求庐镐,前面的請(qǐng)求都會(huì)被忽略掉恩商。
使用iframe方式,簡(jiǎn)單的封閉如下:
var url = 'ztjy://eyJjYWxsIjoibG9naW4iLCJhcmdzIjp7InR5cGUiOiIwIn19';
var iframe = document.createElement('iframe');
iframe.style.width = 0;
iframe.style.height = 0;
iframe.style.display = 'none';
iframe.src = url;
document.body.appendChild(iframe);
setTimeout(function() {
iframe.remove();
}, 100);
攔截 URL SCHEME 的主要流程是:Web 端通過(guò)某種方式(例如 iframe.src)發(fā)送 URL Scheme 請(qǐng)求必逆,之后 Native 攔截到請(qǐng)求并根據(jù) URL SCHEME(包括所帶的參數(shù))進(jìn)行相關(guān)操作怠堪。
在時(shí)間過(guò)程中,這種方式有一定的 缺陷:
使用 iframe.src 發(fā)送 URL SCHEME 會(huì)有 url 長(zhǎng)度的隱患末患。
創(chuàng)建請(qǐng)求研叫,需要一定的耗時(shí)锤窑,比注入 API 的方式調(diào)用同樣的功能璧针,耗時(shí)會(huì)較長(zhǎng)。
但是之前為什么很多方案使用這種方式呢渊啰?因?yàn)樗?支持 iOS6探橱。而現(xiàn)在的大環(huán)境下,iOS6 占比很小绘证,基本上可以忽略隧膏,所以并不推薦為了 iOS6 使用這種 并不優(yōu)雅 的方式。
1.2 Native 調(diào)用 JavaScript
相比于 JavaScript 調(diào)用 Native嚷那, Native 調(diào)用 JavaScript 較為簡(jiǎn)單胞枕,畢竟不管是 iOS 的 UIWebView 還是 WKWebView,還是 Android 的 WebView 組件魏宽,都以子組件的形式存在于 View/Activity 中腐泻,直接調(diào)用相應(yīng)的 API 即可。
Native 調(diào)用 JavaScript队询,其實(shí)就是執(zhí)行拼接 JavaScript 字符串派桩,從外部調(diào)用 JavaScript 中的方法,因此 JavaScript 的方法必須在全局的 window 上蚌斩。
以統(tǒng)計(jì)頁(yè)面停留時(shí)長(zhǎng)為例:
window.onEnter = function (startTime) {
// handler
}
window.onLeave = function (endTime) {
// handler
}
2.Dsbridge
三端易用的現(xiàn)代跨平臺(tái) Javascript bridge铆惑, 通過(guò)它,你可以在Javascript和原生之間同步或異步的調(diào)用彼此的函數(shù)。
DSBridge的設(shè)計(jì)原則就是:讓三端使用方式都是最簡(jiǎn)單的员魏!
主要特點(diǎn):
- Android丑蛤、IOS、Javascript 三端易用撕阎,輕量且強(qiáng)大盏阶、安全且健壯。
- 同時(shí)支持同步調(diào)用和異步調(diào)用
- 支持以類的方式集中統(tǒng)一管理API
- 支持API命名空間
- 支持調(diào)試模式
- 支持API存在性檢測(cè)
- 支持進(jìn)度回調(diào):一次調(diào)用闻书,多次返回
- 支持Javascript關(guān)閉頁(yè)面事件回調(diào)
- Android端支持騰訊X5內(nèi)核
假設(shè)Native端實(shí)現(xiàn)了兩個(gè)api: testSyn名斟、testAsyn。參數(shù)以json傳遞魄眉, testSyn為同步api,執(zhí)行結(jié)束后會(huì)直接返回結(jié)果砰盐,而testAsyn為一個(gè)異步api(可能會(huì)執(zhí)行耗時(shí)操作),執(zhí)行結(jié)束后坑律,結(jié)果異步返回岩梳,同時(shí)注冊(cè)一個(gè)function,供Native端調(diào)用晃择。下面我們看看web端如何調(diào)用冀值。
//同步調(diào)用
var str=dsBridge.call("testSyn",{msg: "testSyn"});
//異步調(diào)用
dsBridge.call("testAsyn",{msg: "testAsyn"}, function (v) {
alert(v);
})
//注冊(cè) javascript API
dsBridge.register('addValue',function(l,r){
return l+r;
})
2.1JavaScript調(diào)用Native
dsBridge.call(method,[arg,callback])
同步或異步的調(diào)用Native API。
method
: Native API 名稱宫屠, 可以包含命名空間列疗。
arg
:傳遞給Native API 的參數(shù)。只能傳一個(gè)浪蹂,如果需要多個(gè)參數(shù)時(shí)抵栈,可以合并成一個(gè)json對(duì)象參數(shù)。
callback(String returnValue)
: 處理Native API的返回結(jié)果. 可選參數(shù)坤次,只有異步調(diào)用時(shí)才需要提供古劲。
2.2注冊(cè)JavaScript api供Native調(diào)用
2.2.1注冊(cè)一個(gè)普通的方法
同步
dsBridge.register(methodName|namespace,function|synApiObject)
異步
dsBridge.registerAsyn(methodName|namespace,function|asyApiObject)
dsBridge.register('addValue',function(l,r){
return l+r;
})
dsBridge.registerAsyn('addValue',function(arg1,arg2,arg3,responseCallback){
responseCallback(arg1+" "+arg2+" "+arg3);
})
2.2.2注冊(cè)一個(gè)對(duì)象,指定一個(gè)命名空間
//namespace test for synchronous
dsBridge.register("test",{
tag:"test",
test1:function(){
return this.tag+"1"
},
test2:function(){
return this.tag+"2"
}
})
//namespace test1 for asynchronous calls
dsBridge.registerAsyn("test1",{
tag:"test1",
test1:function(responseCallback){
return responseCallback(this.tag+"1")
},
test2:function(responseCallback){
return responseCallback(this.tag+"2")
}
})
3.實(shí)際應(yīng)用
1.獲取命名空間(同步)
const namespace = dsbridge.call(``'getNameSpace'``)
2.獲取用戶信息
try {
dsbridge.call(namespace + '.getUserInfo', '', val => {
// 回調(diào)
})
} catch (e) {}
3.跳轉(zhuǎn):jumpTo
try {
dsbridge.call(namespace + '.jumpTo', params, val => {
// 回調(diào)
})
} catch (e) {}
4.注冊(cè)刷新回調(diào)方法缰猴,供客戶端調(diào)用
dsbridge.register(namespace + '.autoRefreshCallback', () => {
// 回調(diào)
})