微信小程序/UniApp蓝牙开发:如何优雅地封装一个可复用的蓝牙通信库(Vue3 Composition API)
Vue3UniApp蓝牙通信库架构设计从零封装高可用BLE管理器在物联网应用爆发式增长的今天蓝牙低能耗BLE技术已成为连接智能设备的首选方案。作为前端开发者当我们面对需要同时控制多台蓝牙打印机、门锁和传感器的商业项目时直接在页面中堆砌蓝牙API调用显然不是可持续的方案。本文将带你从工程化角度构建一个基于Vue3 Composition API的蓝牙通信库解决以下痛点重复代码每个页面都需要重写初始化、连接、监听流程状态混乱多设备连接时状态维护困难异常处理缺失断连重试、错误恢复机制不完善类型安全弱JavaScript的弱类型导致蓝牙操作易出错1. 核心架构设计1.1 分层模型设计一个健壮的蓝牙库应该遵循清晰的分层原则应用层 (UI Components) ↓ 服务层 (Bluetooth Manager) ↓ 适配器层 (Platform API) ↓ 原生层 (iOS/Android/小程序)我们主要聚焦服务层的实现它需要提供以下能力设备发现与连接管理消息收发管道状态机管理错误恢复机制1.2 技术选型对比方案类型优点缺点适用场景Class-based状态集中管理Vue集成不够自然复杂多设备场景Hook-based与Vue3完美契合复杂逻辑拆分困难中小型应用Hybrid模式兼顾灵活与组织性实现复杂度较高本文推荐方案我们采用混合模式用Class管理核心逻辑通过Hook暴露API。// 架构示意 class BluetoothCore { // 设备池管理 private devices: Mapstring, Device new Map() // 连接状态机 private stateMachine: StateMachine new StateMachine() // 平台API适配层 private adapter: BluetoothAdapter uni // 或wx } export function useBluetooth() { const core new BluetoothCore() return { scan: core.scan.bind(core), connect: core.connect.bind(core) } }2. 关键实现细节2.1 设备发现优化策略原始API的startBluetoothDevicesDiscovery存在性能问题我们通过以下策略优化节流搜索自动限制扫描频率设备指纹基于MAC地址和Service UUID过滤缓存机制记住最近连接设备// 设备发现优化实现 async function enhancedDiscovery(options: DiscoveryOptions) { // 合并硬件厂商提供的服务UUID const services [ ...DEFAULT_SERVICES, ...(options.vendorServices || []) ] // 添加防抖逻辑 return new Promise((resolve) { this.discoveryTimer setTimeout(async () { await this.adapter.startBluetoothDevicesDiscovery({ services, success: (res) { this.cacheDevices(res.devices) resolve(res) }, fail: this.errorHandler }) }, 300) // 300ms防抖阈值 }) }2.2 连接状态管理蓝牙连接需要维护复杂的状态流转[Disconnected] ↓ [Discovering] → [Error] ↓ [Connecting] → [Error] ↓ [Connected] ↓ [Disconnecting] → [Error]使用状态机模式封装class ConnectionStateMachine { private states { DISCONNECTED: { connect: CONNECTING }, CONNECTING: { success: CONNECTED, fail: ERROR }, CONNECTED: { disconnect: DISCONNECTING } } private currentState DISCONNECTED transition(action: string) { const nextState this.states[this.currentState][action] if (nextState) { this.currentState nextState return true } return false } }2.3 自动重连机制实现可靠的断线重连需要考虑指数退避重试间隔逐渐增加心跳检测定期检查连接状态最大重试避免无限重连// 重连策略实现 private async handleReconnect(deviceId: string) { let retries 0 const maxRetries 5 while (retries maxRetries) { try { await this.connectDevice(deviceId) return } catch (error) { const delay Math.min(1000 * 2 ** retries, 30000) await new Promise(resolve setTimeout(resolve, delay)) retries } } throw new Error(Max reconnect attempts (${maxRetries}) exceeded) }3. 高级功能实现3.1 多设备并行管理商业场景常需同时控制多台设备我们设计设备池模式class DevicePool { private devices: Mapstring, DeviceContext new Map() addDevice(device: DeviceSpec) { const context { ...device, connection: new Connection(), queue: new OperationQueue() } this.devices.set(device.id, context) } async broadcast(command: Command) { const results await Promise.all( Array.from(this.devices.values()).map( device device.queue.enqueue(() this.sendCommand(device, command) ) ) ) return results } }3.2 消息队列设计蓝牙通信需要严格顺序执行指令我们实现优先级队列class OperationQueue { private pending: Array() Promiseany [] private isProcessing false enqueue(task: () Promiseany, priority 0) { return new Promise((resolve, reject) { const wrappedTask async () { try { const result await task() resolve(result) } catch (error) { reject(error) } } this.pending.splice( this.pending.findIndex(item item.priority priority), 0, { execute: wrappedTask, priority } ) this.processNext() }) } private processNext() { if (this.isProcessing || this.pending.length 0) return this.isProcessing true const task this.pending.shift() task.execute().finally(() { this.isProcessing false this.processNext() }) } }3.3 类型安全增强使用TypeScript定义完整类型体系interface DeviceSpec { id: string name: string services: { main: UUID characteristics: { read: UUID write: UUID notify?: UUID } } } type BluetoothEvent | { type: DEVICE_FOUND; devices: DeviceSpec[] } | { type: CONNECTION_STATE_CHANGE; deviceId: string; state: ConnectionState } | { type: MESSAGE_RECEIVED; deviceId: string; data: ArrayBuffer } class TypedEventEmitter { private listeners new Mapstring, SetFunction() onT extends BluetoothEvent(type: T[type], listener: (event: T) void) { if (!this.listeners.has(type)) { this.listeners.set(type, new Set()) } this.listeners.get(type).add(listener) } }4. 实战应用示例4.1 打印机控制集成封装面向业务的打印机管理器class PrinterManager { constructor(private bluetooth: BluetoothManager) { bluetooth.on(MESSAGE_RECEIVED, (event) { if (event.deviceId this.currentPrinter?.id) { this.handlePrinterResponse(event.data) } }) } async printReceipt(content: ReceiptContent) { const commands this.buildPrintCommands(content) await this.bluetooth.sendInBatch(this.currentPrinter.id, commands) } private buildPrintCommands(content: ReceiptContent): Buffer[] { return [ PrinterCommands.INIT, ...content.lines.map(line Buffer.from(${line}\n, utf-8) ), PrinterCommands.CUT_PAPER ] } }4.2 在UniApp中的使用提供符合Vue使用习惯的Hook// useBluetooth.ts export function useBluetooth() { const devices refDeviceSpec[]([]) const connectionState refConnectionState(disconnected) const manager new BluetoothManager({ onDeviceFound: (newDevices) { devices.value [...devices.value, ...newDevices] }, onStateChange: (state) { connectionState.value state } }) return { devices, connectionState, scan: manager.scan.bind(manager), connect: manager.connect.bind(manager) } }页面中使用script setup const { devices, scan, connect } useBluetooth() onMounted(() { scan({ services: [0000FFE0-0000-1000-8000-00805F9B34FB] }) }) /script template view v-fordevice in devices clickconnect(device.id) {{ device.name }} /view /template4.3 性能优化技巧连接池预热提前初始化常用设备连接指令批处理合并多次写入操作二进制压缩优化数据传输效率// 指令批处理示例 async function sendInBatch(deviceId: string, commands: Buffer[]) { const device this.getDevice(deviceId) const merged Buffer.concat(commands) // 分片发送避免MTU限制 const chunkSize device.mtu - 3 // 保留协议头 for (let i 0; i merged.length; i chunkSize) { const chunk merged.slice(i, i chunkSize) await this.writeCharacteristic(deviceId, chunk) } }在开发过程中我发现蓝牙通信最棘手的不是正常流程而是对各种异常情况的处理。比如当用户突然切换系统蓝牙开关时需要监听bluetoothAdapterStateChange事件并同步更新所有设备状态。经过多次迭代最终形成的这套架构已经在生产环境稳定运行支持同时管理20台蓝牙设备。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2520728.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!