uni-app在app端调用热敏打印机必须走原生插件路径,因蓝牙通信、指令解析、字节流写入等环节受ios/android原生权限、协议及驱动限制;纯js无法绕过。

直接说结论:uni-app 在 App 端调用热敏打印机,不能靠纯 JS 调用 API 实现,必须走原生插件路径——因为蓝牙通信、指令解析、字节流写入等关键环节,iOS/Android 原生层有不可绕过的权限、协议和驱动限制。
为什么 uni.getConnectedBluetoothDevices() 返回空,但设备明明连上了?
这是最常卡住的第一步。现象是:Android 上能搜到、连上、发数据;iOS 上 uni.getConnectedBluetoothDevices() 返回空数组,uni.onBluetoothDeviceFound 完全不触发。
- iOS 的 CoreBluetooth 框架默认过滤掉“非标准服务”的 BLE 设备,热敏打印机的私有 service UUID(如
0000ffe0-0000-1000-8000-00805f9b34fb)不在白名单里,扫描阶段就被静默丢弃 - 必须在
ios/info.plist中添加NSBluetoothAlwaysUsageDescription,且manifest.json → ios → 使用蓝牙必须勾选“是” -
uni.startBluetoothDevicesDiscovery({ services: [] })在 iOS 下无效——services参数必须是非空数组,填你打印机对应的服务 UUID(比如汉印填["0000ffe0-0000-1000-8000-00805f9b34fb"]) - 即使连上,也要确认特征值(
characteristicId)是否具备write权限;很多插件默认读取notify特征,但打印必须写入
ESC/POS 指令发出去,打印机没反应或只出乱码?
不是连接问题,是字节流没对上。热敏打印机不认字符串,只认原始字节——你传 "你好",它看到的是 UTF-8 编码的 6 个字节,但 ESC/POS 指令要求 GBK 编码 + 正确初始化指令头。
- 先确认你的打印机指令集:ESC/POS(常见于芯烨、新大陆)、TSPL(斑马兼容,多见于博思得)、CPCL(Intermec),三者完全不兼容;发错指令轻则空白,重则卡死或反复进纸
- 不要用
encodeURIComponent或TextEncoder直接转字符串——得用专用编码库(如encoding.js)转成 GBK 字节数组,再拼接 ESC 初始化指令(如0x1B 0x40) - 图片打印更复杂:需将 base64 图片解码为 Uint8Array,再按打印机要求做灰度、缩放、行块切割(如每行 384 像素转为 48 字节),最后套用
0x1D 0x76 0x30 0x00等位图指令 - 建议用已验证的指令片段测试:发
new Uint8Array([0x1B, 0x40, 0x1B, 0x61, 0x01, 0x48, 0x65, 0x6C, 0x6C, 0x6F])(居中打印 "Hello"),能出字再加中文
要不要自己封装原生插件?还是用现成插件?
取决于你的打印机型号、交付周期和长期维护成本。现成插件省事,但容易踩坑;自研插件可控,但要啃原生代码。
- 免费插件如
简单打印(插件 ID:12381)支持部分 ESC/POS 打印机,但它把指令封装成 HEX 字符串(如"1b401d57..."),新手难调试,且不处理 iOS 的 service 白名单逻辑 - 商用项目建议封装原生插件:Android 用
BluetoothGatt写特征值写入,iOS 用CBCentralManager+CBPeripheral处理 writeWithoutResponse,并在插件里硬编码你设备的 service/characteristic UUID - 插件接口设计别暴露底层细节:JS 层只传
{ text: "xxx", fontSize: "large", imageBase64: "xxx" },原生层负责转字节、分包、重试、切纸 - 特别注意 Android 8.0+ 的后台蓝牙限制:App 进入后台后,
writeCharacteristic可能失败,需加前台服务保活或提示用户保持前台
真正难的从来不是“连上”,而是让每个字节都精准命中打印机的指令寄存器。指令集文档、UUID 配置、编码转换、分包长度(有些打印机单次最多写 20 字节)——这些细节漏掉任何一环,都会表现为“连接成功但打不出字”。











