打造一款能借助蓝牙模块操控设备并实现双向数据交换的应用程序,听起来颇为炫酷。然而,实际开发过程犹如在布满陷阱的道路上行走。虽然理论看似明了,但实际操作却充满了挑战。这正是我要向大家讲述的uni-app开发蓝牙连接应用过程中的核心经历。
开发初衷
一开始选择研发这款应用,是因为我们的设备需要借助蓝牙进行操控。经过一番市场调研,现成的方案都不太符合我们的需求,于是我们决定自行开发。在个人开发过程中,我希望能找到一个操作简便的开发工具,这时uni-app吸引了我的注意。我原本以为,凭借对相关知识的掌握,开发一个连接蓝牙模块的手机应用应该不难。然而,真正动手后,我才意识到自己当初的想法过于简单了。
//开启蓝牙适配器初始化蓝牙模块
openBluetoothAdapter() {
//刷新蓝牙设备
this.devices=[]
uni.openBluetoothAdapter({
success: (res) => {
console.log("开启蓝牙适配器成功(openBluetoothAdapter success)", res);
this.startBluetoothDevicesDiscovery();
uni.showToast({
title: "开始扫描设备",
icon: "success",
});
},
fail: (res) => {
uni.showToast({
title: "请开启蓝牙",
icon: "none",
});
if (res.errCode === 10001) {
uni.onBluetoothAdapterStateChange(function (res) {
//监听蓝牙适配器是否打开,若打开则自动搜索蓝牙设备(onBluetoothAdapterStateChange)
if (res.available) {
this.startBluetoothDevicesDiscovery();
}
});
}
},
});
},
将开发应用程序作为操控蓝牙模块的工具,这种做法对众多热衷于自主实现此类功能的人颇具吸引力。这样做的好处是不必依赖价格高昂或功能有限的商业软件。然而,在最初规划阶段,并未充分预见uni-app在开发蓝牙功能方面的复杂程度。
初涉uni-app蓝牙开发
刚开始接触开发阶段,我发现uni-app在蓝牙领域的理论指导较为明确。它的API接口看起来逻辑顺畅,但实际操作却显得有些曲折。尽管uni-app和微信小程序在蓝牙开发的API接口大体相同,但细节上仍有不少差异。比如,蓝牙连接的流程,虽然理论上按照文档描述有九个步骤,看起来步骤简单且连贯,涉及连接设备、搜索设备、通信等,理论上按步骤进行即可。但实际开发中,才发现每个步骤都可能遇到难题。
举例来说,以设备查找功能为例,在编写代码实现这一功能时,发现其运行表现极不稳定。常常出现查找失败或检索到众多重复设备的问题。在2023年某月进行的测试中,每进行10次查找,就有5次遭遇重复设备的问题。这导致原本设计的流程被扰乱,不得不重新检查代码的运行逻辑。
//开启蓝牙设备搜索
startBluetoothDevicesDiscovery() {
// 关闭蓝牙适配器的时候将其打开
if (this._discoveryStarted) {
return;
}
this._discoveryStarted = true;
uni.startBluetoothDevicesDiscovery({
allowDuplicatesKey: true,
success: (res) => {
console.log( "开始搜寻蓝牙设备成功(startBluetoothDevicesDiscovery success)", res);
uni.showLoading({
title: "正在搜索设备",
});
this.onBluetoothDeviceFound();
},
});
},
蓝牙打开判断与提示
//监听寻找到新设备的事件
onBluetoothDeviceFound() {
uni.onBluetoothDeviceFound((res)=>{
if(res){
uni.hideLoading();
}
res.devices.forEach(device=>{
//过滤掉没有名字的设备
if (!device.name && !device.localName) {
return
};
//这么操作是为了去除重复
const foundDevices = this.devices//将数据中的数组复制一份,利用动态绑定方式,不断复制最新的数组
const idx = this.inArray(foundDevices, 'deviceId', device.deviceId)
if (idx === -1) {
this.devices.push(device);//数组里没有的的就向里面添加数据,保证没有重复[uni写法]
}
})
console.log(this.devices);
});
},
这部分非常关键,关乎于如何判断蓝牙是否开启。若用户未开启蓝牙,APP自然无法使用相关功能。在开发阶段,如何巧妙设计这一提示尤为关键。我经过多次修改,终于使提示既明确又便于操作,且不会显得过于生硬。这并非只是随意编写一个判断语句那么简单。在开发过程中,我借鉴了众多现有APP的做法,发现有些APP仅是简单提醒用户开启蓝牙,却缺乏友好的引导。
在某个广受欢迎的应用里,仅以文字提示蓝牙未开启,要求用户开启,却只提供了一个极小的按钮,引导用户进入系统设置手动开启蓝牙。这样的操作体验实在不佳。相较之下,我在开发自己的应用时,力求让用户能在应用内轻松完成操作,或者清晰指引他们前往系统设置开启蓝牙,同时还要兼顾不同手机系统的使用习惯。
搜索与设备去重
之前提到搜索结果中设备会重复,这既浪费资源又影响使用感受。我们尝试了多种算法来去除重复设备。起初怀疑是自己搜索的算法出了问题,但后来意识到,蓝牙搜索本身就有这样的特性。
createBLEConnection(e) {
const ds = e.currentTarget.dataset
this.deviceId = ds.deviceId
//将设备名称也传递给全局变量
this.deviceName = ds.name
uni.createBLEConnection({
deviceId:this.deviceId,
success: (res) => {
this.connected=true,
console.log('连接时获取设备id',this.deviceId);
setTimeout(() => {
this.getBLEDeviceServices(this.deviceId);
}, 1000);
},
fail:(err)=>{
uni.showToast({
title:'建立连接失败',
icon:'none'
})
return
}
})
this.stopBluetoothDevicesDiscovery()//此时停止蓝牙搜索
},
在努力改善这一状况时,查阅了蓝牙技术指南,发现问题是由于蓝牙的搜索功能引起的。考虑到没有充足的时间持续搜索不同时间段的数据,便决定采用一个恰当的短暂搜索周期,例如设定为15秒钟。若时间过长,用户可能会觉得等待时间过长;若时间过短,又可能无法找到设备。随后,我们收集了设备信息,并进行了去重处理。
设备特征值的识别与使用
设备的特征值对蓝牙通信至关重要。每台设备都拥有独一无二的标识码,设备内部包含多种服务,每种服务又对应着不同的特征值标识,即uuid。这些特征值还涉及只读、只写、读写兼备、可监听等多种属性。在我进行开发的过程中,发现寻找这些能够实现通信的特征值颇为不易。
我在特定设备上进行了测试,从2023年xx月到xx月,总共进行了近一百次。测试发现,某些特征值表面上看似可以支持读写操作,但实际上,由于其他条件的限制,这些特征值并不能用于通信。经过多次调试,最终才成功找到了适合在服务中使用的特征值,从而实现了通信功能。
延迟调用的关键问题
uni-app里存在一个特别现象,即在特定阶段需要多次进行延迟操作。这种现象在微信小程序中未曾遇到。起初并未察觉这一状况,导致程序频繁出现异常。数据传输始终不畅。
经过查阅众多论坛文章,并向几位资深开发者请教,我才了解到这里需要进行这种特别操作。在实际操作过程中,我尝试了不同的延迟设置以适应不同设备,比如设备A用0.5秒的延迟效果最佳,而设备B则可能需要1秒的延迟。
在开发这个APP的过程中,我积累了不少经验,也经历了很多繁琐的工作。我想问问各位,在你们开发涉及硬件通信的APP时,是否也遇到了许多难题?希望你们能分享自己的见解,同时也欢迎点赞和转发这篇文章。
getBLEDeviceServices(deviceId) {
uni.getBLEDeviceServices({
deviceId:deviceId,
success: (res) => {
//serviceId固定死了
this.getBLEDeviceCharacteristics(deviceId, this.serviceId)
},
fail:(err)=>{
uni.showToast({
title:'获取服务失败',
icon:'none'
})
return
}
})
},