写在前面
- 使用 WebViewJavascriptBridge 框架,将WebViewJavascriptBridge对象由原生端注入到WebView中。
- 前端检验
window.WebViewJavascriptBridge
是否存在 - 通过特定机制(事件监听/iframe) 等待桥接准备就绪
- 使用原生注入的
WebViewJavascriptBridge
对象提供的方法:callHandler
: 调用原生功能registerHandler
: 注册供原生调用的方法
jsBridge编写
新建一个jsBridge文件
js
const u = navigator.userAgent
const isAndroid = u.indexOf('Android') > -1 || u.indexOf('Adr') > -1
/* eslint-disable */
function setupWebViewJavascriptBridge(callback) {
// 第一次调用这个方法的时候,为false
if (window.WebViewJavascriptBridge) {
return callback(WebViewJavascriptBridge)
}
if (isAndroid) {
document.addEventListener('WebViewJavascriptBridgeReady', function () {
callback(WebViewJavascriptBridge)
}, false)
} else {
// 第一次调用的时候,也是false
if (window.WVJBCallbacks) {
return window.WVJBCallbacks.push(callback)
}
// 把callback对象赋值给对象。
window.WVJBCallbacks = [callback]
// 这段代码的意思就是执行加载WebViewJavascriptBridge_JS.js中代码的作用
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)
}
}
/* Android 版本 WebViewJavascriptBridge 的设计要求:
通过调用 init() 方法,建立 JS 端与 Android 原生端的双向通信通道
iOS 版本的 WebViewJavascriptBridge 在桥接对象注入时自动完成初始化,
通过 iframe 机制触发加载后,桥接自动就绪
*/
if (isAndroid) {
setupWebViewJavascriptBridge(function (bridge) {
bridge.init()
})
}
export default {
callhandler(name, data, callback) {
setupWebViewJavascriptBridge(function (bridge) {
bridge.callHandler(name, data, callback)
})
},
registerhandler(name, callback) {
setupWebViewJavascriptBridge(function (bridge) {
bridge.registerHandler(name, function (data, responseCallback) {
callback(data, responseCallback)
})
})
}
}
Android
Android使用 addEventListener
的原因:
- Android WebView 提供了标准的事件机制
- 原生端可以在适当时候触发 JavaScript 事件
- 通过
document.addEventListener
监听自定义事件是一种标准做法
Android原生端在初始化完成后主动触发 WebViewJavascriptBridgeReady
事件通知 JS 端
iOS
iOS 使用 iframe 的原因:
- UIWebView(较老的 iOS WebView)没有像 Android 那样的直接事件触发机制
- 但 WebView 会拦截特定 URL 请求,这是可以利用的特性
使用URL Scheme 拦截机制:
js
var WVJBIframe = document.createElement('iframe')
WVJBIframe.style.display = 'none'
WVJBIframe.src = 'https://__bridge_loaded__'
document.documentElement.appendChild(WVJBIframe)
创建一个隐藏的 iframe 并设置特殊 URL,iOS 原生端通过拦截这个 URL 请求来知道需要初始化桥接。
使用
index.js
js
import jsBridge from './jsBridge'
/**
* methods downloadImage 下载图片
* params list {Array} base64列表parmas = {list: [base64]} base64不要前面的信息 img/png
* title {string}
* */
downloadImage (params, responseCallback) {
jsBridge.callhandler('downloadImage', params, (data) => {
responseCallback(JSON.parse(data))
})
},
/**
* methods checkSmsCode 校验验证码
* params businessCode {string} (modifyDrawPasswordDynamic 銀行卡修改取款密碼
* params strategyCode {string} (ebil_ebilling写死)
* */
checkSmsCode (params, responseCallback) {
jsBridge.callhandler('checkSmsCode', params, (data) => {
let obj = JSON.parse(data)
if (obj.authStatus === 'pass') {
responseCallback(JSON.parse(data))
} else {
Toast.info('驗證碼錯誤_SMS code Error')
}
})
},
/**
* methods 校验账户的交易密码
* params {} 无需传参
* */
getCheckPassword (responseCallback) {
jsBridge.callhandler('getCheckPassword', {}, data => {
if (data) {
responseCallback(JSON.parse(data))
}
})
},
前端页面:
js
getImg () {
this.$bridge.downloadImage({ list: this.url }, () => {
// console.log(res)
})
},
nextPage () {
this.$bridge.checkSmsCode({ businessCode: 'creditAutoAply', strategyCode: 'credit_aply_sms_code' }, res => {
this.$bridge.getCheckPassword(data => {
if (data.authStatus) {
// 保存文件信息
this.$remote(SAVE_FILE_INFO, params).then(res => {
....
})
}
})
})
}
总结
- Android: 利用标准事件机制,原生主动通知 Web
- iOS: 利用 URL 拦截机制,Web 主动触发原生初始化