必须先调用 uni.getbluetoothadapterstate 再操作蓝牙,ios 首次需等授权回调,安卓需处理 discovering 状态;服务与特征值须动态获取,cpcl 指令须用 \r\n 换行,每次 write 前须校验连接。

uni.getBluetoothAdapterState 必须先调用,不能跳过
直接调用 uni.openBluetoothAdapter 后就搜设备,90% 的失败都出在这里。iOS 首次运行会弹授权框,回调还没回来时 getBluetoothAdapterState 就可能返回 poweredOff: true;安卓则常因其他 App 正在扫描,导致 discovering: true,此时再调 startBluetoothDevicesDiscovery 不触发回调。
必须把逻辑写进 getBluetoothAdapterState 的 success 回调里:
- 如果
res.available === false,提示用户去系统设置开蓝牙 - 如果
res.discovering === true,先调uni.stopBluetoothDevicesDiscovery,再启动新搜索 - 不要依赖
openBluetoothAdapter的 success 判断适配器就绪——它不保证底层 ready
服务与特征值不能硬编码,得动态查
很多开发者把服务 UUID 写死成 00001101-0000-1000-8000-00805F9B34FB,结果连上了也发不出指令。CPCL 打印机(如芝柯 ZJ-5890、精臣 Q1)和 ESC/POS 打印机(如佳博、驰腾 CT-320)对服务发现顺序敏感,部分固件要求先发现通用访问服务(00001800-0000-1000-8000-00805F9B34FB)再跳转,或隐藏标准串口服务。
正确做法是链式调用:
-
uni.createBLEConnection成功后,加setTimeout延迟 600–800ms(尤其红米 Note 系列等有服务缓存延迟的机型) - 再调
uni.getBLEDeviceServices,检查res.services是否为空,为空则重连 - 拿到服务列表后,用
uni.getBLEDeviceCharacteristics查特征值,只选properties.write === true && properties.notify === false的项(CPCL 是单向写入)
CPCL 指令必须用 \r\n 换行,不能用 \n
拼接 CPCL 指令时用 "TEXT 24 0 30 50 Hello" + "\n" + "BARCODE 128",打印机大概率只打出第一行。底层 BLE socket(尤其是 Android 原生层)严格识别 \r\n 作为指令分隔符,\n 会被吞掉或忽略。
发送前务必统一换行:
- 所有 CPCL 行末尾补
\r\n,包括最后一行 - 避免字符串拼接时漏掉,建议用数组
.join('\r\n')构建完整指令流 - 不要混用 ZPL 语法(如
^XA开头),CPCL 解析器不识别
每次 write 前必须 checkConnection
连接成功不等于持续可用。iOS 后台切前台时连接常已断开,安卓某些省电策略会主动回收 BLE 连接。直接调 uni.writeBLECharacteristicValue 报错 10009: connection is not established 就是这个原因。
安全写法是:
- 封装一个
safeWrite函数,内部先调uni.getConnectedBluetoothDevices - 检查返回数组是否包含目标
deviceId,不包含就重连 - 重连成功后再执行 write,不要假设“刚连上就能发”
- 对连续多条指令,每条之间加 20–50ms 间隔,避免 MTU 溢出或底层丢包
最易被忽略的是服务发现后的延迟等待和 write 前的连接校验——这两步在真机上几乎必现问题,但模拟器完全测不出来。











