Vue H5页面与Android和IOS交互(Vue部分)
美好的结局总有一个良好的开端
最近在调研一个需求,需要通过移动数据采集基站与安卓平板通信,言外之意就是我有一个基站,当我的平板插上USB数据线后,我期望通过USB将基站数据发送到平板,在平板上进行一系列的操作。说实话,一开始真是毫无头绪,和后端讨论了很久,最初看到了H5+的SQLite
库,决定用uniapp来做一个前端控制的数据库,一切操作都由前端控制,奈何咱们这个系统面向对象是学校,几千个学生的运动检测,一包数据由若干条组成。考虑到所有的操作都在前端进行着实难度太大而且影响性能,果断放弃了SQLite,那怎么办呢????
初识jsBridge
某天早上,后端发给我了一个git仓库说这个好像可以实现我们的混合开发,对于没有接触过这方面需求的我刚看到也挺懵,不过既然可行,那就试试吧...
js调用原生方法/原生调用js方法
这里首先说明一下,我们这篇文章不会出现安卓代码,我们以解决问题为目的,不会有太多与文章无关的内容出现,也是为了让大家一目了然这么一个操作顺序,所以想了解安卓编码的同学可以直接看看上面提到的仓库。
其实仓库里写的很清楚咱们js应该怎么样去写,所以我就直接上代码
function setupWebViewJavascriptBridge(callback) {
if (window.WebViewJavascriptBridge) {
return callback(window.WebViewJavascriptBridge)
}
if (window.WVJBCallbacks) {
return window.WVJBCallbacks.push(callback)
}
window.WVJBCallbacks = [callback]
let WVJBIframe = document.createElement('iframe')
WVJBIframe.style.display = 'none'
WVJBIframe.src = 'https://__bridge_loaded__'
document.documentElement.appendChild(WVJBIframe)
setTimeout(() => {
document.documentElement.removeChild(WVJBIframe)
}, 0)
}
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)
})
})
}
}
这里面把WebViewJavaScriptBridge封装了一下,就可以直接在外部调用callhandler和registerhandler方法了
由于我这里是使用的vue3并且只是在一个组件里使用到了,所以就没有进行全局注册了
import bridge from '@/config/bridge'
onMounted(() =>
bridge.registerhandler('getNowTime', (data, responseCallback) => {
dataMap.value = JSON.parse(data)
var responseData = '我收到了数据'
responseCallback(responseData)
})
})
const getData = () => {
console.log('接收数据')
bridge.callhandler('submitFromWeb', { id: '123' }, (response) => {
resData.value = response
console.log("Javascript was loaded by Android and successfully loaded.");
})
}
好的,其实到这里都很简单,上面提及到的仓库都有教大家如何去编写js调用原生和原生调用js,但问题也就出在这里。至今我都不知道他的demo是如何跑通的,我们按照他的实例代码,写入我们自己的项目会发现接收不到回调,这是为什么呢?在js中注册了方法,在ios和android两个平台使用,结果是ios调用ok,android死活调用不了,最重要的是js去调用android的方法却可以。那我们就去看看安卓到底怎么回事,查阅了一些资料后得知在安卓上面需要init
,所以我们改造一下
if (/(iPhone|iPad|iPod|iOS)/i.test(navigator.userAgent)) {
setupWebViewJavascriptBridge(function(bridge){
$bridge = bridge
})
}else if(/(Android)/i.test(navigator.userAgent)) {
connectWebViewJavascriptBridge(function(bridge){
bridge.init((message, responseCallback) => {
responseCallback('get message success')
})
$bridge = bridge
})
}
加上这一个判断,如果是Android我们就使用brige.init()
来执行方法,根据vue-bridge-webview
这个插件最终将我们的代码改写为如下形式
var bridgeConfig = {
bridgeWebViewDelay : 0,
callHandle : {}, // bridge android / ios
silent : false
}
var $bridge = {
registerHandler : function (name,callback) {
if(bridgeConfig.silent){
console.log(name,' register handler failure')
}
},
callHandler : function (name, params, callback) {
if(bridgeConfig.silent){
console.log(name,' call handler webView failure')
}
}
};
function setupWebViewJavascriptBridge (callback) {
if (window.WebViewJavascriptBridge) {
return callback(window.WebViewJavascriptBridge)
}
if (window.WVJBCallbacks) {
return window.WVJBCallbacks.push(callback)
}
window.WVJBCallbacks = [callback]
let WVJBIframe = document.createElement('iframe')
WVJBIframe.style.display = 'none'
WVJBIframe.src = 'https://__bridge_loaded__'
document.documentElement.appendChild(WVJBIframe)
setTimeout(() => {
document.documentElement.removeChild(WVJBIframe)
}, 0)
}
/* 用于创建桥接对象的函数 , android 初始化 */
function connectWebViewJavascriptBridge(callback) {
//如果桥接对象已存在,则直接调用callback函数
if (window.WebViewJavascriptBridge) {
callback(WebViewJavascriptBridge)
}
//否则添加一个监听器来执行callback函数
else {
document.addEventListener('WebViewJavascriptBridgeReady', function () {
callback(WebViewJavascriptBridge)
}, false)
}
}
if (/(iPhone|iPad|iPod|iOS)/i.test(navigator.userAgent)) {
setupWebViewJavascriptBridge(function(bridge){
$bridge = bridge
})
}else if(/(Android)/i.test(navigator.userAgent)) {
connectWebViewJavascriptBridge(function(bridge){
bridge.init((message, responseCallback) => {
responseCallback('get message success')
})
$bridge = bridge
})
}
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)
})
})
}
}
如此一来,我们就可以成功的执行安卓的回调啦~不过这里至今我还是不太明白为什么。好啦,方法也能执行了,数据也能传递了,但后端这个时候又抛出了一个问题来了,他说这个插件,安卓在执行方法的时候一直占用的是主线程,对于我们每一秒钟都要调用方法来说会很卡,实际效果也是如此,所以他重新又找了个插件,SDBridgeJava,半个月前才推送到git上的新插件,这个插件就能实现异步请求并且放在子线程中执行,这样一来就能大大提高我们的性能,说干就干,其实和上面的写法也大同小异,只是在接收数据的时候对data的解析不同罢了,对于WebViewJavaScriptBridge
的封装就可以直接还原到最顶部的写法,接受数据和传递数据则变为了{key:value}的形式,非常简单,这里就直接贴代码:
onMounted(() => {
bridge.registerhandler('getNowTime', (data, responseCallback) => {
dataMap.value = JSON.parse(data.AndroidKey)
var responseData = '我收到了数据'
responseCallback({'msg': responseData})
})
})
const getData = () => {
console.log('接收数据')
bridge.callhandler('submitFromWeb', { id: '123' }, (response) => {
resData.value = response.result
console.log("Javascript was loaded by Android and successfully loaded.");
})
}
由于我们的项目确定是在安卓平板进行开发,故没有对设备进行判断,如果需要在IOS上开发的话可以按照SDBridegJava这个插件的文档在接收数据的时候,response.result里进行判断:
if (result === "iOS") {/* ... */}
else if (result === "Android") { /* ... */}
正如文档所说:JS tries to call the native method to judge whether it has been loaded successfully and let itself know whether its user is in android app or IOS app
到底为止,我们就可以成功的和原生进行数据传递以及通信了。不过当我们打包后又会出现问题:
安卓访问我们的页面是通过webview.loadUrl()
访问我们的页面,当我们打包后安卓放在自己的项目里路径就不再是http开头而是file,但file协议并不支持,此时是会跨域的,不过安卓是可以配置跨域的,这个也不用我们前端去操心。但是我们会发现,尽管配置好了跨域以后,但是我们的图片资源,我们的js资源却访问不到,这真是一步一个坑啊,一番查阅资料后,将vite.config.ts
下base: '/'改为'./'
再打包后,发现就可以访问到我们的资源了。真是一波三折啊~~~~
至此,我们Vue H5页面与原生Android的交互也就实现了,并且由于使用了SDBridgeJava这个插件,传输数据也不会有卡顿的现象。对于我来说这也算是一个新知识,所以记录一下。
转载自:https://juejin.cn/post/7102292121893879821