无
- 发布:2020-09-10 18:56
- 更新:2024-08-09 16:57
- 阅读:4213
产品分类: uniapp/App
PC开发环境操作系统: Windows
PC开发环境操作系统版本号: 1909
HBuilderX类型: 正式
HBuilderX版本号: 2.8.11
手机系统: Android
手机系统版本号: Android 9.0
手机厂商: OPPO
手机机型: 无
页面类型: vue
打包方式: 云端
项目创建方式: HBuilderX
操作步骤:
预期结果:
希望更新取消监听的方法
希望更新取消监听的方法
实际结果:
无
无
bug描述:
uniapp蓝牙里的监听事件,没有相对应的取消监听的方法,导致在安卓环境下多次监听,而且调用uni.closeBluetoothAdapter()关闭蓝牙模块后,监听依然存在。我看微信小程序都有对应的取消监听的方法wx.offBLEConnectionStateChange、wx.offBLECharacteristicValueChange、wx.offBLEPeripheralConnectionStateChanged,麻烦官方也跟进一下
是的,再次重复监听的时候会得到重复蓝牙,还会多次累加,罗盘、加速度计都会这样,大大的坑,官方也不回复。我现在是把重复的数据丢掉,坑啊。。。。。。。。。。。。
给你看我处理的办法
'''
uni.onBluetoothDeviceFound(devices => { //开启 监听寻找到新设备的事件
//console.log("发现设备: " + JSON.stringify(devices)); //JSON.stringify() 方法用于将 JavaScript 值转换为 JSON 字符串
if (!self.deviceList.some(item => { //不重复,就添加到devicesList中,
return item.deviceId === devices.devices[0].deviceId
})) {
if (devices.devices[0].name != "") { //保存蓝牙名称不为空的蓝牙设备参数
self.deviceList.push ({
name : devices.devices[0].name,
deviceId: devices.devices[0].deviceId,
RSSI : devices.devices[0].RSSI,
})
//蓝牙自动链接,当手机靠近蓝牙设备达到一定距离时,依据蓝牙名称和RSSI的强度来判断链接蓝牙设备
if(devices.devices[0].name === "HMSoft" || devices.devices[0].name === "BBC micro:bit [gavop]"){
if(devices.devices[0].RSSI > -48){
self.createBLEConnection(devices.devices[0].deviceId);
}
}
}
}
});
'''
-
Yukin (作者)
回复 Jalen_cokoino: 老哥,我现在的做法是把所有的监听都放在app.vue这个文件的onLaunch生命周期里(onBLEConnectionStateChange、onBLECharacteristicValueChange、onBluetoothDeviceFound),这样进app的时候就只会监听一次,然后在具体的页面就只需要做连接蓝牙、获取服务、获取特征值和发指令的操作就行了
2020-09-15 15:00
麻烦提供个示例,我们复现一下。
-
uni.onBluetoothDeviceFound(devices => { //开启 监听寻找到新设备的事件
console.log("发现设备: " + JSON.stringify(devices)); //JSON.stringify() 方法用于将 JavaScript 值转换为 JSON 字符串
});
多次成功进行调用这个API,将监听的数据打印出来,就可以看得到了
2020-09-21 10:50
-
加速度计的这个API也有这个现象:
uni.onAccelerometerChange(function (res) {
console.log(res.x);
console.log(res.y);
console.log(res.z);
});2020-09-21 10:57
<template>
<view class="content">
<button @click="search">搜索蓝牙</button>
<button @click="unsearch">停止搜索蓝牙</button>
<button @click="compass">开启罗盘</button>
<button @click="closecompass">关闭罗盘</button>
<button @click="Accelerometer">开启加速度计</button>
<button @click="closeAccelerometer">关闭加速度计</button>
</view>
</template>
<script>
export default {
data() {
return {
title: 'Hello'
}
},
onLoad() {
},
methods: {
search(){
//开启手机蓝牙模块:
uni.openBluetoothAdapter({
success: res => {
console.log("蓝牙已开启");
uni.startBluetoothDevicesDiscovery({
success: res => {
console.log("开始监听设备...");
uni.onBluetoothDeviceFound(devices => {
//重复搜索会打印重复相同数据,还会累加,多了会卡死机。
console.log("发现设备: " + JSON.stringify(devices));
})
},
})
},
})
},
unsearch(){
uni.stopBluetoothDevicesDiscovery({
success: e => {
console.log("停止蓝牙设备搜索");
},
})
},
compass(){
uni.startCompass();
uni.onCompassChange(function (res) {
//重复搜索会打印重复相同数据,还会累加,多了会卡死机。
console.log(res.direction);
});
},
closecompass(){
uni.stopCompass();
},
Accelerometer(){
uni.startAccelerometer();
uni.onAccelerometerChange(function (res) {
//重复搜索会打印重复相同数据,还会累加,多了会卡死机。
console.log("x:"+res.x);
console.log("y:"+res.y);
console.log("z:"+res.z);
});
},
closeAccelerometer(){
uni.stopAccelerometer();
}
}
}
</script>
<style>
.content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.logo {
height: 200rpx;
width: 200rpx;
margin-top: 200rpx;
margin-left: auto;
margin-right: auto;
margin-bottom: 50rpx;
}
.text-area {
display: flex;
justify-content: center;
}
.title {
font-size: 36rpx;
color: #8f8f94;
}
</style>
代码复现
我们也出此案这个问题,不止你说的音频组件也有这个问题,我们的蓝牙部分现在做法也是丢数据,尤其是用匿名函数接收,没办法在页面被销毁后函数也会被销毁,最后我们采用动态判断的方案,我们在音频里面自己实现了全局的音频注册管理,目前音频没出现这个问题
在main.js里面全局挂载
import bgAudioMannager from '@/utils/helper/audioMannager.js'
Vue.prototype.bgAudioMannager = bgAudioMannager
然后在实际使用页面的
onLoad() {
let callback = {
onTimeUpdate: (playId,duration,currentTime,buffered) => {
//this.onTimeUpdateOnTimeUpdate(playId,duration,currentTime,buffered)
},
onPlay: (playId) => {
console.log('播放')
},
onPause: (playId) => {
console.log('暂停')
},
onStop: (playId) => {
console.log('播放停止')
},
onEnded: (playId) => {
console.log('播放结束')
},
onPrev: () => {
console.log('上一首触发')
},
onNext: () => {
console.log('下一首触发')
}
}
//index为自生页面的唯一标识
this.bgAudioMannager.initdCallback(callback,'index')
}
然后是全局代码
const initdCallback = (callback,id) => {
console.log("应用注册音频回调",id)
returnCallback[id] = callback;
}
const destroy = (index) => {
try{
returnCallback[index] = null;
delete returnCallback[index];
}catch(e){
//TODO handle the exception
}
}
returnCallback在一开始初始化的时候做回调调用
写一个调用demo
bgAudioMannager.onCanplay(() => {
console.log("音频加载成功")
for (let item in returnCallback) {
try{
returnCallback[item].onCanplay(bgPlayList[playIndex].id)
}catch(e){
//TODO handle the exception
}
}
})
已经完美解决,这段代码放到app.vue的onlanuch 里面
uni.onBLECharacteristicValueChange (c){
//将监听到的值转发
uni.$emit('value',c)
}
//**别的页面
uni.$on('value',function{
//处理逻辑
})
onUnload(){
uni.$off('value')
}
发现安卓app和ios app还有微信小程序中会有不同表现,目前封装了这个,使用了单例模式
import eventbus from '@/utils/eventbus'
import utils from '@/utils/utils'
import useConfigStore from '@/store/modules/config'
import i18nConfig from '@/locale/index'
const { t } = i18nConfig.global
class Bluetooth {
constructor() {
if (Bluetooth.instance) {
return Bluetooth.instance;
}
Bluetooth.instance = this;
this.limitUUid = undefined
this.serviceUUid = null
this.platform = null
this.bleList = [];
this._discoveryStarted = false;
// 重发
this.times = 0
this.blePubTimer = null;
// 蓝牙重启计时器
this.bleAdpterTimer = null;
this.bleUpdateTime = 5000;
// 蓝牙设备
this.clearConnectInfo()
// 回调函数
this.readCb = null;
this.connectionStateChangeCb = null;
this.deviceFoundCb = null;
// 设置回调函数(在这里设置不会重复进入)
this.onBLECharacteristicValueChange();
this.onConnectionStateChange()
this.onBluetoothDeviceFound();
}
clearConnectInfo(){
this.serviceId = null;
this.characteristicId = null;
this.bluetoothId = null;
this.connect = false;
this.connecting = false;
}
startBle(deviceFoundCb, refresh) {
this.platform = useConfigStore().getConfig().platform
setTimeout(() => {
// 不同平台需要分开处理
// #ifdef MP
this.doOpenBluetoothAdapter(deviceFoundCb);
// #endif
// #ifdef APP-PLUS
console.log(this.platform, "refresh", refresh)
if (this.platform === "ios") {
if(refresh === false){
this.doOpenBluetoothAdapter(deviceFoundCb);
}else {
this.bleUpdateTime = 30000;
this.openBluetoothAdpter4IosApp(deviceFoundCb);
}
}
if (this.platform === "android") {
this.doOpenBluetoothAdapter(deviceFoundCb);
}
// #endif
}, 1300);
if(this.blePubTimer){
return
}
this.blePubTimer = setInterval(() => {
if (this.bleList && this.bleList.length > 0) {
this.bleList = this.bleList.filter((item) => {
if(!item.last_time){
item.last_time = Date.now();
}
return Date.now() - item.last_time < this.bleUpdateTime;
});
eventbus.pub("bleList", {
bleList: this.bleList,
});
// console.log(this.bleList)
}
}, 1500);
}
async doOpenBluetoothAdapter(deviceFoundCb) {
uni.openBluetoothAdapter({
success: (res) => {
console.log("初始化蓝牙适配器成功", res);
this.startBluetoothDevicesDiscovery(deviceFoundCb); // 开始搜索
},
fail: (err) => {
console.log("初始化错误", err);
if (err?.errCode === 10001 || err?.code === 10001) {
// 10001:当前蓝牙适配器不可用
uni.showModal({
title: t('public.button'),
content: t('public.enableBluetooth'),
showCancel: false,
});
}
},
});
}
clearTimer(){
clearInterval(this.bleAdpterTimer);
}
openBluetoothAdpter4IosApp(deviceFoundCb) {
// ios app如果不重新开启蓝牙适配器则无法获取蓝牙数据
clearInterval(this.bleAdpterTimer)
console.log("ios 打开蓝牙适配器")
this.bleAdpterTimer = setInterval(()=>{
if (this.bleList && this.bleList.length > 0) {
return
}
uni.closeBluetoothAdapter({
success: (res) => {
console.log("closeBluetoothAdapter 成功")
},
fail: (err) => {
console.log("closeBluetoothAdapter 失败", err)
}
})
this.doOpenBluetoothAdapter(deviceFoundCb)
}, 3500);
}
async startBluetoothDevicesDiscovery(deviceFoundCb) {
this.deviceFoundCb = deviceFoundCb;
uni.startBluetoothDevicesDiscovery({
powerLevel: "high",
allowDuplicatesKey: true,
services: ['FFF0'],
success: (res) => {
console.log("开始搜索", res);
// #ifdef MP
this.onBluetoothDeviceFound();
// #endif
},
fail: (res) => {
console.log("搜索失败", res);
wx.showModal({
title: "提示",
content: "搜索失败,请重试",
showCancel: true,
});
},
});
}
onBluetoothDeviceFound() {
console.log("设置设备发现回调")
uni.onBluetoothDeviceFound((res) => {
// console.log("found res", res.devices)
res.devices.forEach((device, index) => {
if (!device.name && !device.localName) {
return;
}
// if (!utils.isDevice(device.name)) {
// return;
// }
if (this.platform === "ios") {
device.mac = utils.uuid2mac(device.advertisData);
device.bluetoothId = device.deviceId
}
if (this.platform === "android") {
device.mac = device.deviceId;
device.bluetoothId = device.deviceId;
}
device.name = device.name.trim();
const idx = utils.inArray(this.bleList, "deviceId", device.deviceId);
if (idx === -1) {
this.bleList.push(device);
} else {
const oldDevice = {
...this.bleList[idx],
};
this.bleList[idx] = {
...oldDevice,
last_time: new Date(),
RSSI: device.RSSI,
};
}
});
if (this.deviceFoundCb) {
this.deviceFoundCb(res.devices);
}
});
}
connectDevice(bluetoothId, needStop,connectBeforeCb , connectSucCb,
connectFailCb,
connectionStateChangeCb,
closeCallback, notifyCb, readCb, serviceUUid = 'FFF0', limitUUid = undefined) {
this.connectionStateChangeCb = connectionStateChangeCb
this.readCb = readCb
this.clearTimer()
console.log('尝试连接蓝牙', bluetoothId)
if (this.connect) {
console.log('蓝牙已连接');
return
}
if(this.connecting){
console.log('蓝牙正在连接');
return;
}
if(connectBeforeCb){
connectBeforeCb()
}
this.connecting = true
this.bluetoothId = bluetoothId
this.serviceUUid = serviceUUid
this.limitUUid = limitUUid
uni.createBLEConnection({
deviceId: bluetoothId,
// timeout: 2000,
timeout: 3500,
success: (res) => {
if (needStop) {
uni.stopBluetoothDevicesDiscovery({
success: (res) => {
console.log('停止搜寻蓝牙', res);
}
}
);
}
if(connectSucCb){
console.log('蓝牙连接成功回调');
setTimeout(() => {
connectSucCb()
}, 4000)
}
console.log('连接低功耗蓝牙成功', res);
// 设置蓝牙最大传输单元
// 如果设置超过了系统的最大值就返回错误码-1,显示 internal error 内部错误
// 但好像设置依旧有效,可能如果超过了系统的最大值,默认使用系统支持的最大值
if (this.platform === 'android') {
setTimeout(() => {
uni.setBLEMTU({
deviceId: bluetoothId,
mtu: 185,
success: (res1) => {
console.log('设置蓝牙最大传输单元', res1);
setTimeout(() => {
this.getBLEDeviceServices(bluetoothId, notifyCb, readCb);
}, 1500);
},
fail: (err) => {
this.closeBLEConnection(closeCallback);
console.log('设置蓝牙最大传输单元 失败', err);
}
});
}, 1000)
} else {
setTimeout(() => {
this.getBLEDeviceServices(bluetoothId, notifyCb, readCb);
}, 1000)
}
},
fail: (err) => {
console.log('蓝牙连接失败');
if(connectFailCb){
console.log('蓝牙连接失败回调');
connectFailCb()
}
// TODO: 连接失败的处理
this.closeBLEConnection(closeCallback);
},
complete:()=>{
this.connecting = false;
}
})
}
onConnectionStateChange() {
console.log("设置状态改变回调")
uni.onBLEConnectionStateChange(({deviceId, connected}) => {
console.log(`device ${deviceId} state has changed, connected: ${connected}`)
this.connect = connected;
if (this.connectionStateChangeCb) {
this.connectionStateChangeCb(deviceId, connected)
}
});
}
closeBLEConnection(closeCallback) {
console.log("尝试断开")
uni.closeBLEConnection({
deviceId: this.bluetoothId,
success: (result) => {
console.log('断开成功', result);
// this.clearConnectInfo()
if(closeCallback){
closeCallback()
}
},
fail: (error) => {
console.log('断开失败', error, this.bluetoothId);
},
complete: () =>{
this.connecting = false
this.connect = false
}
});
}
getBLEDeviceServices(bluetoothId, notifyCb, readCb) {
uni.getBLEDeviceServices({
deviceId: bluetoothId,
success: (res) => {
console.log('获取蓝牙设备所有服务', res);
console.log('所有服务', res.services);
for (let i = 0; i < res.services.length; i++) {
if (res.services[i].uuid.indexOf(this.serviceUUid) > -1) {
console.log('res.services[i].uuid', res.services[i].uuid);
if (res.services[i].isPrimary) {
// setTimeout(()=>{
// this.getBLEDeviceCharacteristics(bluetoothId, res.services[i].uuid, notifyCb, readCb);
// }
// ,1000) }
this.getBLEDeviceCharacteristics(bluetoothId, res.services[i].uuid, notifyCb, readCb);
}
}
}
},
fail(error) {
console.log('获取蓝牙设备所有服务失败', error);
this.closeBLEConnection();
}
});
}
getBLEDeviceCharacteristics(deviceId, serviceId, notifyCb, readCb) {
var that = this;
uni.getBLEDeviceCharacteristics({
deviceId,
serviceId,
success: (res) => {
console.log('获取蓝牙设备某个服务中所有特征值 成功', res.characteristics);
for (let i = 0; i < res.characteristics.length; i++) {
let item = res.characteristics[i];
if (this.limitUUid) {
if (item.uuid.indexOf(this.limitUUid) == -1) {
return
}
}
if (item.properties.read) {
console.log('read', item.uuid);
// 读取低功耗蓝牙设备的特征值的二进制数据值。注意:必须设备的特征值支持 read 才可以成功调用。
uni.readBLECharacteristicValue({
deviceId,
serviceId,
characteristicId: item.uuid,
success(res) {
console.log('开启读取特征值成功', res);
},
fail(err) {
console.log('开启读取特征值失败', err);
}
});
}
if (item.properties.write) {
this.serviceId = serviceId;
this.characteristicId = item.uuid;
console.log('write', item.uuid);
}
if (item.properties.notify || item.properties.indicate) {
console.log('notify', item.uuid);
// 启用低功耗蓝牙设备特征值变化时的 notify 功能,订阅特征值。
// 注意:必须设备的特征值支持 notify 或者 indicate 才可以成功调用。
// 另外,必须先启用 notifyBLECharacteristicValueChange 才能监听到设备 characteristicValueChange 事件
// #ifdef MP
that.onBLECharacteristicValueChange();
// #endif
uni.notifyBLECharacteristicValueChange({
deviceId,
serviceId,
characteristicId: item.uuid,
state: true,
success(res) {
console.log('启用notify成功', res);
// TODO: callback
if (notifyCb) {
console.log('notify回调');
notifyCb(res);
}
}
});
}
}
},
fail(err) {
console.error('获取蓝牙服务的特征值 失败', err);
}
});
}
onBLECharacteristicValueChange() {
// 判断是否有设备连接上
console.log('设置 读取数据回调');
// 操作之前先监听,保证第一时间获取数据
// 大概操作就是做了某段操作后把_currentState改为该阶段,然后在收到C ACK这些指令时都判断_currentState,同一阶段收到不同的指令做出不同动作
uni.onBLECharacteristicValueChange((characteristic) => {
// console.log('收到的消息', Date.now(), characteristic)
console.log("收到的值:", utils.ab2hex(characteristic.value));
if (this.readCb) {
console.log('收到消息回调');
this.readCb(characteristic)
}
});
//
// /*#ifdef APP-PLUS*/
//
// if (readCb) {
// readCb()
// }
// /*#endif*/
}
writeBLEData(bluetoothId,buffer, successCb, failCb) {
// console.log("write buffer",buffer)
// console.log(this.serviceId)
// console.log(this.characteristicId)
// console.log(bluetoothId)
console.log("buffer", utils.ab2hex(buffer))
var that = this
/*#ifdef MP*/
uni.writeBLECharacteristicValue({
deviceId: bluetoothId,
serviceId: this.serviceId,
characteristicId: this.characteristicId,
value: buffer,
success(res) {
console.log("写入蓝牙数据成功")
if(successCb){
successCb(res)
}
},
fail(err) {
console.log("写入蓝牙数据失败", err)
// setTimeout(() => {
// if (that.times != 0) return
// that.times++
// that.writeBLEData(bluetoothId, buffer, null, null)
// }, 2000)
if (failCb){
failCb(err)
}
}
});
/*#endif*/
/*#ifdef APP-PLUS*/
uni.writeBLECharacteristicValue({
deviceId: bluetoothId,
serviceId: this.serviceId,
characteristicId: this.characteristicId,
value: buffer,
success(res) {
console.log("写入蓝牙数据成功")
that.times = 0
if(successCb){
successCb(res)
}
},
fail(err) {
console.log("写入蓝牙数据失败", err)
setTimeout(() => {
if (that.times != 0) return
that.times++
that.writeBLEData(bluetoothId, buffer, null, null)
}, 2000)
if (failCb){
failCb(err)
}
}
});
/*#endif*/
}
async getBluetoothAdapterState() {
return new Promise((resolve, reject) => {
uni.getBluetoothAdapterState({
success(e) {
resolve(e)
},
fail(e) {
console.log("获取蓝牙适配器状态失败:",e)
reject(e)
}
})
})
}
}
const bluetooth = new Bluetooth();
export default bluetooth;
参考各位达人,我的经验分享如下:
1、ESP32 作为BLE服务器,将BLE客户端发来数据返回,作为调试手机APP的工具
2、编写一个公共文件ble_api.js,实现蓝牙功能。编写一个类,类中存储BLE通信数据和方法,定义此类唯一一个实例,供网页.vue调用。
3、为了解决uniapp蓝牙无法注销回调函数,在ble_api.js就BLE uni.notify编写唯一一个回调函数,保证唯一回调函数(这里是在网页显示周期内)注册一次。否者会出现多次调用问题。
4、在回调函数中,使用uni.$emit公共函数,将BLE数据发送至具体网页.vue中。
5、经实验具体做法,在每个网页script内, onshow()中调用BLE类实例注册一次BLE uni.notify的回调函数。否者,要么BLE不接收数据,要么回调函数重复多次执行。 BLE uni.notify的回调函数中使用uni.$emit()转发BLE数据,在具体网页.vue的 onshow()使用uni.$on()监听接收与处理数据,在此具体网页.vue的onhide()中使用uni.$off()注销此监听。
代码可参考:https://www.cnblogs.com/excellentHellen/p/18279222
目前uni-app,给出 :
/**
- 需要基础库:
2.9.0
- 在插件中使用:需要基础库
2.9.1
- 移除蓝牙低功耗设备的特征值变化事件的监听函数
- 文档: https://developers.weixin.qq.com/miniprogram/dev/api/device/bluetooth-ble/wx.offBLECharacteristicValueChange.html
*/
offBLECharacteristicValueChange(listener?: UniNamespace.OffBLECharacteristicValueChangeCallback): void;
我在实际调用时,好像没什么效果,不知道是调用方法不对,还是这就是个假的
1***@qq.com
怎么可能呢?
2024-04-01 18:54