☰
uni-app跨平台开发中经典蓝牙连接方案实践
2026/10/31 10:07:29 网站建设 项目流程

1. 项目背景与需求分析

在uni-app跨平台开发中,蓝牙功能是连接智能硬件的关键桥梁。官方提供的蓝牙API主要面向BLE(低功耗蓝牙)协议,但实际开发中我们经常遇到需要连接经典蓝牙设备(如打印机、POS机、工业设备等)的场景。这些设备通常采用SPP(Serial Port Profile)协议传输数据,而plus.bluetooth模块原生并不支持这类设备的直接连接。

我最近在开发一个零售行业的移动应用时,就遇到了需要连接传统蓝牙小票打印机的需求。经过多次实践,总结出一套在uni-app中通过plus对象操作经典蓝牙的完整方案。这套方案的核心思路是:利用Native.js桥接Android/iOS原生蓝牙API,实现经典蓝牙设备的发现、配对、连接和数据传输。

2. 技术方案设计

2.1 经典蓝牙与BLE的核心区别

经典蓝牙(BR/EDR)和低功耗蓝牙(BLE)在协议栈层面有本质差异:

特性经典蓝牙BLE
协议栈SPP/HFP/A2DP等GATT/ATT
连接方式需要配对直接连接
数据传输流式传输(类似Socket)特征值读写
典型功耗较高极低
适用场景音频/大数据传输传感器/状态上报

2.2 技术路线选择

由于uni-app的plus.bluetooth模块仅支持BLE协议,我们需要通过以下方式实现经典蓝牙功能:

  1. Android平台:通过Native.js调用Android原生的BluetoothAdapter和BluetoothSocket API
  2. iOS平台:通过Native.js调用CoreBluetooth框架(需特殊处理)
  3. 统一封装:将平台差异封装成统一的JavaScript接口

3. Android平台实现详解

3.1 环境准备

首先在manifest.json中添加必要权限:

{ "permissions": { "Bluetooth": { "description": "经典蓝牙连接" }, "BluetoothAdmin": { "description": "蓝牙设备管理" } } }

3.2 设备发现与配对

const bluetoothAdapter = plus.android.importClass('android.bluetooth.BluetoothAdapter'); const btAdapter = bluetoothAdapter.getDefaultAdapter(); // 检查蓝牙是否开启 if (!btAdapter.isEnabled()) { const intent = plus.android.importClass('android.content.Intent'); const action = bluetoothAdapter.ACTION_REQUEST_ENABLE; const requestIntent = new intent(action); plus.android.runtimeMainActivity.startActivityForResult(requestIntent, 1); } // 开始设备发现 btAdapter.startDiscovery(); // 监听设备发现广播 const receiver = plus.android.implements('io.dcloud.android.content.BroadcastReceiver', { onReceive: function(context, intent) { const action = intent.getAction(); if (action === bluetoothAdapter.ACTION_FOUND) { const device = plus.android.invoke(intent, 'getParcelableExtra', bluetoothDevice.EXTRA_DEVICE); console.log('发现设备:', device.getName(), device.getAddress()); } } }); const filter = new plus.android.importClass('android.content.IntentFilter')(bluetoothAdapter.ACTION_FOUND); plus.android.runtimeMainActivity.registerReceiver(receiver, filter);

3.3 设备连接与通信

建立SPP连接的核心代码:

function connectToDevice(macAddress) { const uuid = plus.android.importClass('java.util.UUID'); const sppUuid = uuid.fromString("00001101-0000-1000-8000-00805F9B34FB"); const btDevice = btAdapter.getRemoteDevice(macAddress); const btSocket = plus.android.invoke(btDevice, 'createRfcommSocketToServiceRecord', sppUuid); try { btSocket.connect(); const outStream = plus.android.invoke(btSocket, 'getOutputStream'); const inStream = plus.android.invoke(btSocket, 'getInputStream'); // 发送数据示例 const data = "Hello Bluetooth"; plus.android.invoke(outStream, 'write', data.getBytes()); // 接收数据示例 const buffer = plus.android.newArray('byte', 1024); const bytes = plus.android.invoke(inStream, 'read', buffer); const received = String.fromCharCode.apply(null, buffer.slice(0, bytes)); console.log('收到数据:', received); } catch (e) { console.error('连接失败:', e); } }

4. iOS平台实现方案

iOS平台对经典蓝牙的支持较为有限,需要通过ExternalAccessory框架实现MFi认证设备连接。对于非MFi设备,可尝试以下方案:

const eam = plus.ios.importClass('EAAccessoryManager'); const manager = eam.sharedAccessoryManager(); // 获取已配对设备列表 const connectedAccessories = manager.connectedAccessories(); for (let i = 0; i < connectedAccessories.count(); i++) { const accessory = connectedAccessories.objectAtIndex(i); console.log('设备:', accessory.name(), accessory.serialNumber()); } // 建立连接(需要设备支持指定的协议) const protocolString = "com.example.protocol"; const session = plus.ios.importClass('EASession').alloc() .initWithAccessoryForProtocol(accessory, protocolString); const outStream = session.outputStream(); const inStream = session.inputStream();

5. 跨平台封装方案

为统一调用接口,建议封装以下核心方法:

const bluetooth = { // 初始化蓝牙适配器 initAdapter: function() { if (plus.os.name === 'Android') { // Android实现 } else { // iOS实现 } }, // 扫描设备 startDiscovery: function(callback) { // 各平台实现 }, // 连接设备 connect: function(address, success, fail) { // 各平台实现 }, // 发送数据 write: function(data) { // 各平台实现 }, // 接收数据 onDataReceived: function(callback) { // 各平台实现 } }

6. 实战案例:蓝牙打印机连接

以连接ESC/POS指令的小票打印机为例:

6.1 打印机初始化序列

const initCommands = [ 0x1B, 0x40, // 初始化打印机 0x1B, 0x61, 0x00, // 左对齐 0x1D, 0x21, 0x00 // 标准字号 ]; function sendInitSequence() { const buffer = new ArrayBuffer(initCommands.length); const view = new Uint8Array(buffer); initCommands.forEach((cmd, i) => view[i] = cmd); bluetooth.write(buffer); }

6.2 打印文本内容

function printText(text) { const encoder = new TextEncoder(); const encoded = encoder.encode(text); bluetooth.write(encoded); // 换行并走纸 const feed = new Uint8Array([0x0A, 0x0A]); bluetooth.write(feed.buffer); }

7. 常见问题与解决方案

7.1 Android连接失败问题

问题现象:调用createRfcommSocketToServiceRecord时抛出IOException

解决方案:

  1. 检查设备是否已完成配对
  2. 尝试使用反射获取BluetoothSocket:
const clazz = plus.android.invoke(btDevice, 'getClass'); const method = plus.android.invoke(clazz, 'getMethod', 'createRfcommSocket', plus.android.importClass('java.lang.Integer').TYPE); const socket = plus.android.invoke(method, 'invoke', btDevice, 1);

7.2 数据传输不稳定

优化建议:

  1. 设置适当的MTU大小(Android默认约20字节)
  2. 实现数据分包发送机制
  3. 添加数据校验和重传逻辑
function sendData(data, chunkSize = 20) { for (let i = 0; i < data.length; i += chunkSize) { const chunk = data.slice(i, i + chunkSize); bluetooth.write(chunk); // 添加适当延迟 await new Promise(resolve => setTimeout(resolve, 50)); } }

7.3 iOS兼容性问题

注意事项:

  1. 需要在Info.plist中添加Supported external accessory protocols
  2. 设备必须经过MFi认证
  3. 部分功能需要企业证书才能使用

8. 性能优化建议

  1. 连接池管理:对频繁连接的设备保持长连接
  2. 数据缓冲:实现发送队列避免数据丢失
  3. 心跳检测:定期检查连接状态
  4. 错误恢复:自动重连机制实现
class BluetoothManager { constructor() { this.queue = []; this.isSending = false; } send(data) { this.queue.push(data); if (!this.isSending) { this.processQueue(); } } async processQueue() { if (this.queue.length === 0) { this.isSending = false; return; } this.isSending = true; const data = this.queue.shift(); try { await bluetooth.write(data); this.processQueue(); } catch (e) { console.error('发送失败:', e); setTimeout(() => this.processQueue(), 1000); } } }

9. 安全注意事项

  1. 蓝牙通信默认不加密,敏感数据需自行加密
  2. 设备配对需用户确认,不能自动完成
  3. 及时释放蓝牙资源,避免电量消耗
  4. 在onUnload中关闭所有连接
window.addEventListener('unload', () => { if (btSocket && btSocket.isConnected()) { btSocket.close(); } if (receiver) { plus.android.runtimeMainActivity.unregisterReceiver(receiver); } });

10. 扩展思路

  1. 蓝牙双模设备支持:同时兼容BLE和经典蓝牙
  2. 协议抽象层:支持多种打印机指令集
  3. 自动重连:网络异常后的自动恢复
  4. 传输压缩:对大尺寸数据进行压缩传输

这套方案已在多个商业项目中验证,包括零售POS系统、工业PDA应用等场景。虽然uni-app官方没有直接提供经典蓝牙支持,但通过Native.js桥接原生API的方式,我们仍然能够实现完整的蓝牙通信功能。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询