前言:
因为现在对移动端动态化的需求越来越大,在许多APP中一般都会使用webview控件去嵌入一些web页面,从而去避免频繁的发版问题。但有时web页面是需要去调用原生的一些功能,这时候就需要JavaScript与Native之间去进行一些交互。
交互方式:
一、通过schema方式交互。
二、客户端向webview注入变量供JavaScript调用。
三、通过桥接方式交互。
方式1:schema方式交互
这种方式一般用于较为简单的交互行为(不需要返回值),比如在web页面上点击一个按钮去调用客户端的回退页面操作,那么步骤大概如下:
1.前端与客户端约定好这个schema名称(现在与iOS约定的名称是objc)。
2.前端通过window.location.href = “objc://goback” 的方式去发起请求。
3.客户端拦截objc这个schema,然后去解析要执行的方法具体是什么(这里是goback),然后执行相应的操作。
4.如果需要向客户端传递参数在后边直接拼接即可,如: “objc://goback:/参数1:/参数2″,客户端解析即可。注意:如果参数是字符串,务必使用encodeURIComponent先进行编码;参数也可以是一个JS全局函数,客户端执行完原生方法后去调用这个全局函数,也可实现回调功能,但这种方式不太优雅。
方式2:向webview注入变量
这种方式就是客户端将原生方法直接注入到当前webview中,可以直接通过JavaScript调用,对前端来说较为方便。
例如:
客户端向我们的web页面中注入了这样一个对象:
window.test = {
hello:function(){
console.log('hello test')
}
}
我们直接在页面中调用即可:
if(window.test){
window.test.hello();
}
方式三:通过桥接方式交互
当web页面与客户端交互较为复杂时,需要各种回调参数,以及两端之间的相互调用;这种情况我们可以通过引入第三方库来实现,当然也需要客户端引入相应的包。
Android:JsBridge
引入第三方库:
在web页面中引入这两个库,并根据设备系统去加载对应的JS,如下:
//获取浏览器版本
function Versions() {
var u = navigator.userAgent,
app = navigator.appVersion;
return {
trident: u.indexOf('Trident') > -1, //IE内核
presto: u.indexOf('Presto') > -1, //opera内核
webKit: u.indexOf('AppleWebKit') > -1, //苹果、谷歌内核
gecko: u.indexOf('Gecko') > -1 && u.indexOf('KHTML') == -1, //火狐内核
mobile: !!u.match(/AppleWebKit.*Mobile.*/) || !!u.match(/AppleWebKit/), //是否为移动终端
ios: !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/), //ios终端
android: u.indexOf('Android') > -1 || u.indexOf('Linux') > -1, //android终端或者uc浏览器
iPhone: u.indexOf('iPhone') > -1 || u.indexOf('Mac') > -1, //是否为iPhone或者QQHD浏览器
iPad: u.indexOf('iPad') > -1, //是否iPad
webApp: u.indexOf('Safari') == -1, //是否web应该程序,没有头部与底部
weiXin: u.indexOf('MicroMessenger') > -1 //是否是微信
};
}
let devices = '';
//根据设备类型执行相应的桥接代码
if (Versions().mobile && Versions().android) {
devices = "android"
; (function () {
if (window.WebViewJavascriptBridge) { return }
var messageHandlers = {}
var responseCallbacks = {}
var uniqueId = 1
function init(messageHandler) {
if (WebViewJavascriptBridge._messageHandler) { throw new Error('WebViewJavascriptBridge.init called twice') }
WebViewJavascriptBridge._messageHandler = messageHandler
}
function send(data, responseCallback) {
_doSend({ data: data }, responseCallback)
}
function registerHandler(handlerName, handler) {
messageHandlers[handlerName] = handler
}
function callHandler(handlerName, data, responseCallback) {
_doSend({ handlerName: handlerName, data: data }, responseCallback)
}
function _doSend(message, responseCallback) {
console.log("responseCallback:" + responseCallback);
if (responseCallback) {
var callbackId = 'cb_' + (uniqueId++) + '_' + new Date().getTime()
responseCallbacks[callbackId] = responseCallback
message['callbackId'] = callbackId
}
console.log("sending:" + JSON.stringify(message));
if (typeof _WebViewJavascriptBridge == "undefined") {
return;
}
_WebViewJavascriptBridge._handleMessageFromJs((typeof message.data == 'string') && message.data.constructor == String ? message.data : JSON.stringify(message.data) || null, message.responseId || null,
message.responseData || null, message.callbackId || null, message.handlerName || null);
}
function _dispatchMessageFromJava(messageJSON) {
var message = JSON.parse(messageJSON)
var messageHandler
if (message.responseId) {
var responseCallback = responseCallbacks[message.responseId]
if (!responseCallback) { return; }
responseCallback((typeof message.responseData == 'string') && message.responseData.constructor == String ? message.responseData : JSON.parse(message.responseData))
delete responseCallbacks[message.responseId]
} else {
var responseCallback
if (message.callbackId) {
var callbackResponseId = message.callbackId
responseCallback = function (responseData) {
_doSend({ responseId: callbackResponseId, responseData: responseData })
}
}
var handler = WebViewJavascriptBridge._messageHandler
if (message.handlerName) {
handler = messageHandlers[message.handlerName]
}
try {
handler(message.data, responseCallback)
} catch (exception) {
if (typeof console != 'undefined') {
console.log("WebViewJavascriptBridge: WARNING: javascript handler threw.", message, exception)
}
}
}
}
function _handleMessageFromJava(messageJSON) {
_dispatchMessageFromJava(messageJSON)
}
//export
window.WebViewJavascriptBridge = {
init: init,
send: send,
registerHandler: registerHandler,
callHandler: callHandler,
_handleMessageFromJava: _handleMessageFromJava
}
//dispatch event
var doc = document;
var readyEvent = doc.createEvent('Events');
readyEvent.initEvent('WebViewJavascriptBridgeReady');
readyEvent.bridge = WebViewJavascriptBridge;
doc.dispatchEvent(readyEvent);
})();
} else {
(function () {
window.setupWebViewJavascriptBridge = function (callback) {
if (window.WebViewJavascriptBridge) { return callback(WebViewJavascriptBridge); }
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)
}
})()
devices = "ios"
}
封装调用方法
JS调用客户端方法
/**
* @method js调用原生客户端方法
* @param {String} method 方法名
* @param {Object} params 参数对象
* @param {Function} call 回调函数
*/
let JsCallNative = function (method,params, call) {
if (devices == "ios") {
window.setupWebViewJavascriptBridge(function (bridge) {
bridge.callHandler(method , params ,
function (response) {
if (typeof call == "function") {
call(response);
}
})
});
} else {
window.WebViewJavascriptBridge.callHandler(method , params ,
function (response) {
if (typeof call == "function") {
call(JSON.parse(response));
}
})
}
};
参数说明:
1.method:两端约定好的方法名
2.params:需要传递给客户端的参数
3.call : 回调参数,如果需要回调值或者回调操作即可传入
客户端调用JS方法
/**
* @method 原生客户端调用Js方法
* @param {String} method 方法名
* @param {Function} call 回调函数
*/
let NativeCallJs = function (method, call) {
if (devices == "ios") {
window.setupWebViewJavascriptBridge(function (bridge) {
bridge.registerHandler(method, function (data, responseCallback) {
var responseData = { 'Javascript Says': 'Right back atcha!' }
responseCallback(responseData);
if (typeof call == "function") {
responseData = call(data);
}
responseCallback(responseData)
})
});
} else {
window.WebViewJavascriptBridge.registerHandler(method, function (data, responseCallback) {
var responseData = { 'Javascript Says': 'Right back atcha!' }
if (typeof call == "function") {
responseData = call(JSON.parse(data));
}
responseCallback(responseData)
});
}
};
参数说明:
1.method 约定的方法名
2.call 回调函数
其他
DSBridge 是一个三端易用现代跨平台JavaScript bridge,可实现JavaScript与原生之间同步或异步的调用彼此的函数,能够保持前端一套代码在两端运行。
相关链接: