介绍
原生APP
跳转到H5
页面时,往往需要携带一些用户信息,之前做法是在跳转的地址中拼接H5
页面需要的参数,现在通过window.WebViewJavascriptBridge
悄悄的进行数据交互。
- 安卓环境:如果不存在
window.WebViewJavascriptBridge
变量,需要手动添加Dom
的WebViewJavascriptBridgeReady
事件监听;- ios环境:判断是否存在
window.WebViewJavascriptBridge
变量,如果不存在继续判断window.WVJBCallbacks
变量,如果仍然不存在,则手动赋值为H5
回调,然后document.createElement('iframe')
插入document
中,随即移除- 以上处理完成后,通过
WebViewJavascriptBridge
变量来注册【事件】以便APP
能监听到并执行相应操作- 需要对安卓系统进行
init
处理,如果是安卓系统,注册事件之前需要先调用WebViewJavascriptBridge.init()
- 注意: 一个页面整个生命周期过程中,只能进行一次
init()
否则会报错,我的做法是通过一个全局变量来判断是否初始化过
基本实现
// 针对安卓和ios系统,对window.WebViewJavascriptBridge进行兼容性处理
function setupWebViewJavascriptBridge(callback) {
var u = navigator.userAgent
var isAndroid = u.indexOf('Android') > -1 || u.indexOf('Adr') > -1
var isiOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/)
if (window.WebViewJavascriptBridge) { return callback(WebViewJavascriptBridge) }
if (isAndroid) {
document.addEventListener('WebViewJavascriptBridgeReady', function() {
callback(WebViewJavascriptBridge)
}, false);
}
if (isiOS) {
if (window.WVJBCallbacks) { return window.WVJBCallbacks.push(callback); }
window.WVJBCallbacks = [callback];
var WVJBIframe = document.createElement('iframe');
WVJBIframe.style.display = 'none';
WVJBIframe.src = 'https://__bridge_loaded__';
document.documentElement.appendChild(WVJBIframe);
setTimeout(function () {
document.documentElement.removeChild(WVJBIframe)
}, 0)
}
}
setupWebViewJavascriptBridge(function(bridge) {
var u = navigator.userAgent
var isAndroid = u.indexOf('Android') > -1 || u.indexOf('Adr') > -1
var isiOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/)
// 自己定义的全局变量,用来判断当前页面中安卓系统是否进行过一次初始化,页面卸载时取消赋值
if (!window.hasCalledWindowWebViewJavascriptBridge && isAndroid) {
window.hasCalledWindowWebViewJavascriptBridge = true
bridge.init(function(message, responseCallback) {
console.log('JS got a message', message)
responseCallback(message)
})
}
// js注册一个给原生APP捕获的方法 DemoToJSBridge。注册后,原生APP就可以直接访问
bridge.registerHandler('DemoToJSBridge', function(data, responseCallback) {
console.log('native 传过来的是:', data)
responseCallback(data)
})
// JSToDemoBridge 已经在原生APP注册好了,js直接调用
bridge.callHandler('JSToDemoBridge', params, function(response) {
console.log(response)
})
})
示例
- 前端定义一个
DemoToJSBridge
,供原生APP调用。- 原生APP定义一个
JSToDemoBridge
,供javascript
调用- 传递的参数中格式:
{ action: '', data: {/* ... */}}
,其中action
用来标识操作,data
是传递的数据- 前端在全局挂载一个
$Demo
,需要调用APP的原生方式时,直接$Demo.xxx()
这么调用- 前端的
jsBridge
方法不能污染全局环境,可以使用自执行函数!(function() { /* ... */})()
- 定义前端方法和
action
的对应关系
var protocol = {
// 前端去调用原生APP的方法。比如:$Demo.getToken(),对应的action就是:get:token
call: {
setTopBarShare: "topBar:share",
openShareBar: "shareBar:open",
shareToHHmessage: "shareTo:hhmessage",
shareToWXtimeline: "shareTo:wxtimeline",
shareToWXmessage: "shareTo:wxmessage",
shareToQQ: "shareTo:qq",
shareToQzone: "shareTo:qzone",
getToken: "get:token"
},
// 原生APP主动去调用js的方法是,对应的响应。比如action为share:qzone,会执行前端的onShareQZone()方法
event: {
onShareHHMessage: "share:hhmessage",
onShareWxTimeline: "share:wxtimeline",
onShareWxMessage: "share:wxmessage",
onShareQQ: "share:qq",
onShareQZone: "share:qzone"
}
}
- 把对应的前端方法挂载到
$Demo
下面
!(function(w) {
var protocol = {
call: {
setTopBarShare: "topBar:share",
openShareBar: "shareBar:open",
shareToHHmessage: "shareTo:hhmessage",
shareToWXtimeline: "shareTo:wxtimeline",
shareToWXmessage: "shareTo:wxmessage",
shareToQQ: "shareTo:qq",
shareToQzone: "shareTo:qzone",
getToken: "get:token"
},
event: {
onShareHHMessage: "share:hhmessage",
onShareWxTimeline: "share:wxtimeline",
onShareWxMessage: "share:wxmessage",
onShareQQ: "share:qq",
onShareQZone: "share:qzone"
}
}
var h = {}
var eventMap = {}
// 原生APP主动触发的方法
for (var key in protocol.event) {
!(function() {
var action = protocol.event[key]
// $Demo.onShareQZone
h[key] = function(cb) {
eventMap[action] = function(data) {
cb(data)
}
}
})()
}
for (var key in protocol.call) {
h[key] = function(cb) {
console.log('not in app')
}
}
w.$Demo = h
})(this)
// 这里用 this,在浏览器环境代表window,在node环境代表global
- 编写
setupWebViewJavascriptBridge
function setupWebViewJavascriptBridge(callback) {
if (window.WebViewJavascriptBridge) { return callback(WebViewJavascriptBridge); }
if (window.WVJBCallbacks) { return window.WVJBCallbacks.push(callback); }
document.addEventListener('WebViewJavascriptBridgeReady', function() { callback(WebViewJavascriptBridge) }, false);
window.WVJBCallbacks = [callback];
var WVJBIframe = document.createElement('iframe');
WVJBIframe.style.display = 'none';
WVJBIframe.src = 'https://__bridge_loaded__';
document.documentElement.appendChild(WVJBIframe);
setTimeout(function() { document.documentElement.removeChild(WVJBIframe) }, 0);
}
- 注册方法
function setupEvent(configData, callFun) {
for (var key in protocol.call) {
(function() {
var action = protocol.call[key]
h[key] = function(data, cb) {
callFun(action, data, cb)
}
})()
}
configData.onShareHHMessage && h.onShareHHMessage(configData.onShareHHMessage);
configData.onShareWxTimeline && h.onShareWxTimeline(configData.onShareWxTimeline);
configData.onShareWxMessage && h.onShareWxMessage(configData.onShareWxMessage);
configData.onShareQQ && h.onShareQQ(configData.onShareQQ);
configData.onShareQZone && h.onShareQZone(configData.onShareQZone);
}
h.config = function(configData, configCb) {
setupWebViewJavascriptBridge(function(bridge) {
// js端注册方法,给APP调用
bridge.registerHandler('DemoToJSBridge', function(data, responseCallback) {
var event = eventMap[data.action];
if(!event){
responseCallback({ 'result': 201 });
return;
}
event(data.data);
responseCallback({ 'result': 0 });
})
// js调用APP方法
setupEvent(configData, function(action, data, cb) {
bridge.callHandler('JSToDemoBridge', { action: action, data: data }, function(response) {
cb(response)
})
})
configCb()
})
}
- 完整代码如下
!(function(w) {
var protocol = {
call: {
setTopBarShare: "topBar:share",
openShareBar: "shareBar:open",
shareToHHmessage: "shareTo:hhmessage",
shareToWXtimeline: "shareTo:wxtimeline",
shareToWXmessage: "shareTo:wxmessage",
shareToQQ: "shareTo:qq",
shareToQzone: "shareTo:qzone",
getToken: "get:token"
},
event: {
onShareHHMessage: "share:hhmessage",
onShareWxTimeline: "share:wxtimeline",
onShareWxMessage: "share:wxmessage",
onShareQQ: "share:qq",
onShareQZone: "share:qzone"
}
};
var h = {}
var eventMap = {}
for (var key in protocol.event) {
(function() {
var action = protocol.event[key]
h[key] = function(cb) {
eventMap[action] = function(data) {
cb(data)
}
}
})()
}
for (var key in protocol.call) {
h[key] = function(cb) {
console.log('not in app')
}
}
function setupEvent(configData, callFun) {
for (var key in protocol.call) {
(function() {
var action = protocol.call[key]
h[key] = function(data, cb) {
callFun(action, data, cb)
}
})()
}
configData.onShareHHMessage && h.onShareHHMessage(configData.onShareHHMessage);
configData.onShareWxTimeline && h.onShareWxTimeline(configData.onShareWxTimeline);
configData.onShareWxMessage && h.onShareWxMessage(configData.onShareWxMessage);
configData.onShareQQ && h.onShareQQ(configData.onShareQQ);
configData.onShareQZone && h.onShareQZone(configData.onShareQZone);
}
function setupWebViewJavascriptBridge(callback) {
if (window.WebViewJavascriptBridge) { return callback(WebViewJavascriptBridge); }
if (window.WVJBCallbacks) { return window.WVJBCallbacks.push(callback); }
document.addEventListener('WebViewJavascriptBridgeReady', function() { callback(WebViewJavascriptBridge) }, false);
window.WVJBCallbacks = [callback];
var WVJBIframe = document.createElement('iframe');
WVJBIframe.style.display = 'none';
WVJBIframe.src = 'https://__bridge_loaded__';
document.documentElement.appendChild(WVJBIframe);
setTimeout(function() { document.documentElement.removeChild(WVJBIframe) }, 0);
}
w.$Demo = h
h.config = function(configData, configCb) {
setupWebViewJavascriptBridge(function(bridge) {
// js端注册方法,给APP调用
bridge.registerHandler('DemoToJSBridge', function(data, responseCallback) {
var event = eventMap[data.action];
if(!event){
responseCallback({ 'result':201});
return;
}
event(data.data);
responseCallback({ 'result':0});
})
// js调用APP方法
setupEvent(configData, function(action, data, cb) {
bridge.callHandler('JSToDemoBridge', { action: action, data: data }, function(response) {
cb(response)
})
})
configCb()
})
}
})(this)
- 使用示例
window.$Demo.config({}, () => {
window.$Demo.getToken({ key: userKey }, (e) => {
console.log(e)
})
})
})
发表评论