更新记录
1.0.0(2026-11-05)
- 首个版本:支持 uni-app 与 uni-app x(Android)。
- SDK 生命周期:
init/isInitialized/release/destroy/setDebugMode。 - 适配器:
getBluetoothAdapter/isBluetoothOn/getPairedDevices。 - 搜索与配对:
startDiscovery/stopDiscovery/isDiscovering/getBondState/createBond/removeBond/clearBondDevices。 - 多连接管理:
createConnection/connect/disconnectConnection/disconnectAllConnections/releaseConnection/releaseAllConnections/getConnection/getConnections/getOrderedConnections/getFirstConnection/getLastConnection/clearQueue。 - 数据写入:
write(hex/base64/text三选一,支持tag回执、immediate插队、viaObserver事件回执)。 - 事件观察者:
registerObserver/unregisterObserver/isObserverRegistered,事件涵盖蓝牙开关、搜索、配对、连接状态与数据收发。 - 配对状态事件
onBondStateChanged:createBond/removeBond/clearBondDevices以及系统设置里的配对变化都会推送 10 / 11 / 12,不依赖连接对象是否存在。 - 运行时权限:
requestPermissions/checkPermissions,提供逐项授予状态与永久拒绝列表。 - 连接超时保护:
connect默认 12 秒超时,超时主动断开并返回9270019。
平台兼容性
uni-app(4.72)
| Vue2 | Vue2插件版本 | Vue3 | Vue3插件版本 | Chrome | Safari | app-vue | app-vue插件版本 | app-nvue | app-nvue插件版本 | Android | Android插件版本 | iOS | 鸿蒙 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| √ | 1.0.0 | √ | 1.0.0 | × | × | √ | 1.0.0 | √ | 1.0.0 | 5.0 | 1.0.0 | × | × |
| 微信小程序 | 支付宝小程序 | 抖音小程序 | 百度小程序 | 快手小程序 | 京东小程序 | 鸿蒙元服务 | QQ小程序 | 飞书小程序 | 小红书小程序 | 快应用-华为 | 快应用-联盟 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| × | × | × | × | × | × | × | × | × | × | × | × |
uni-app x(4.72)
| Chrome | Safari | Android | Android插件版本 | iOS | 鸿蒙 | 微信小程序 |
|---|---|---|---|---|---|---|
| × | × | 5.0 | 1.0.0 | × | × | × |
zy-classic-bluetooth
插件介绍
安卓经典蓝牙(Bluetooth Classic / SPP)UTS 原生插件,支持 uni-app 与 uni-app x(仅 Android)。提供多设备并发连接、自定义服务 UUID、写入队列与 tag 回执、搜索配对、连接状态机与双向数据收发能力,可直接与蓝牙串口模块、单片机、传感器、打印机等设备通信。
使用场景
- 一台手机同时连接多台经典蓝牙串口设备,并行收发互不干扰。
- 工业设备「一问一答」通信:每次写入带
tag,用回执里的tag与耗时对应请求与响应。 - 设备使用非标准 SPP 服务 UUID 的透传通道,需要在连接时指定自定义 UUID。
- 需要在界面上区分「配对中 / 已配对 / 已连接 / 已释放」等精细状态的设备管理页。
使用流程
- 调用
requestPermissions申请扫描、连接、定位权限(Android 12 及以上也需定位权限,见「权限说明」)。 - 调用
init初始化,onEvent中接收蓝牙开关、搜索、配对、连接、数据等全部事件。 - 用
getPairedDevices取已配对设备,或startDiscovery搜索新设备(结果走onDeviceFound事件)。 - 需要时用
createBond发起配对,配对过程与结果通过onBondStateChanged事件观察(11 配对中 / 12 已配对),也可用getBondState主动查询。 - 调用
createConnection创建连接实例(可选),再调用connect发起连接;success在真正连接成功时回调。 - 用
write发送数据,通过回调或onWrite事件拿到写入结果。 - 用
disconnectConnection断开(可重连)或releaseConnection释放(需重新创建)。 - 页面卸载时调用
destroy释放全部资源。
用法
import {
init,
destroy,
requestPermissions,
checkPermissions,
getBluetoothAdapter,
isBluetoothOn,
getPairedDevices,
startDiscovery,
stopDiscovery,
isDiscovering,
createConnection,
connect,
disconnectConnection,
disconnectAllConnections,
releaseConnection,
releaseAllConnections,
getConnection,
getConnections,
getOrderedConnections,
getFirstConnection,
getLastConnection,
clearQueue,
write,
getBondState,
createBond,
removeBond,
clearBondDevices
} from '@/uni_modules/zy-classic-bluetooth'
权限
requestPermissions({
success: (res) => {
// res.allGranted 是否全部授予
// res.list 每项 { name, granted }
// res.permanentlyDenied 被永久拒绝的权限(需引导用户到系统设置手动开启)
console.log('权限结果', res)
},
fail: (err) => console.error(err.errCode, err.errMsg)
})
// 只检查不弹框
checkPermissions({
success: (res) => console.log(res.allGranted)
})
初始化与事件
init({
debug: false, // 是否输出底层调试日志,默认 false
threadMode: 'main', // 事件与回调线程:main / posting / background / async
onEvent: (event) => {
switch (event.type) {
case 'onBluetoothAdapterStateChanged':
console.log('蓝牙开关状态', event.adapterState) // 10 关闭 / 11 正在开启 / 12 已开启 / 13 正在关闭
break
case 'onDeviceFound':
console.log('发现设备', event.name, event.address, event.rssi, event.bondState)
break
case 'onDiscoveryStart':
case 'onDiscoveryStop':
console.log('搜索状态变化', event.type)
break
case 'onDiscoveryError':
console.log('搜索错误', event.discoveryErrorCode, event.message) // 0~4
break
case 'onBondStateChanged':
// 配对状态:10 未配对 / 11 配对中 / 12 已配对(createBond / removeBond / 系统设置变化都触发)
console.log('配对状态', event.address, event.bondState)
break
case 'onConnectionStateChanged':
// 0 未连接 / 1 连接中 / 2 配对中 / 3 已配对 / 4 已连接 / 5 已释放
console.log('连接状态', event.address, event.uuid, event.state)
break
case 'onRead':
console.log('收到数据', event.hex, event.text, event.length)
break
case 'onWrite':
// 仅 write 传 viaObserver: true 时下发
console.log('写入结果', event.tag, event.success)
break
}
},
success: () => console.log('初始化完成'),
fail: (err) => console.error(err.errCode, err.errMsg)
})
搜索与配对
getPairedDevices({
success: (res) => console.log('已配对', res.list) // [{ name, address, bondState, type, rssi }]
})
startDiscovery({
success: () => console.log('已发起搜索'),
fail: (err) => console.error(err.errCode, err.errMsg) // 9270004 蓝牙未开启 / 9270005 缺扫描权限 / 9270007 缺定位权限
})
stopDiscovery({ success: () => console.log('已停止搜索') })
createBond({
address: '00:11:22:33:44:55',
success: () => console.log('配对请求已发起')
})
getBondState({
address: '00:11:22:33:44:55',
success: (res) => console.log('配对状态', res.bondState) // 10 / 11 / 12
})
removeBond({
address: '00:11:22:33:44:55',
success: () => console.log('已请求解除配对')
})
连接
// 连接(不传 uuid 时使用默认 SPP UUID 00001101-0000-1000-8000-00805f9b34fb)
connect({
address: '00:11:22:33:44:55',
uuid: '00001101-0000-1000-8000-00805f9b34fb', // 可选,自定义服务 UUID
timeout: 12000, // 可选,连接超时毫秒,默认 12000,超时回 9270019
success: (conn) => {
// conn: { address, name, uuid, state, connected, released, bondState }
console.log('已连接', conn)
},
fail: (err) => console.error(err.errCode, err.errMsg)
})
// 只创建连接实例,不发起连接
createConnection({ address: '00:11:22:33:44:55' })
// 查询
getConnections({ success: (res) => console.log('全部连接', res.list) })
getOrderedConnections({ success: (res) => console.log('按创建顺序', res.list) })
getFirstConnection({ success: (conn) => console.log(conn) })
getLastConnection({ success: (conn) => console.log(conn) })
getConnection({ address: '00:11:22:33:44:55', success: (conn) => console.log(conn.state) })
// 断开(可重连)
disconnectConnection({ address: '00:11:22:33:44:55' })
disconnectAllConnections()
// 释放(不可重连,需要重新创建)
releaseConnection({ address: '00:11:22:33:44:55' })
releaseAllConnections()
数据收发
// 文本(UTF-8)
write({
tag: 'req1', // 写入标识,回执中原样返回
text: 'hello',
success: (res) => console.log('写入成功', res.tag, res.length, res.cost + 'ms')
})
// 十六进制(空格、逗号、短横线、冒号、0x 前缀会被忽略)
write({
address: '00:11:22:33:44:55', // 多连接时指定目标,省略时使用第一个连接
tag: 'req2',
hex: '01 02 FF',
immediate: true // 插入队首优先发送
})
// base64
write({ tag: 'req3', base64: 'AQIDBA==' })
// 写入结果走事件(不传回调时可用于统一日志)
write({ tag: 'req4', hex: 'AA BB', viaObserver: true })
// 清空待发送队列
clearQueue({ address: '00:11:22:33:44:55' })
释放
destroy({ success: () => console.log('已销毁,可重新 init') })
插件方法
| 方法 | 说明 | 回调 |
|---|---|---|
init(options) |
初始化,注册事件观察者与系统蓝牙广播监听 | success / onEvent(持续) / fail |
isInitialized(options) |
是否已初始化 | success:{ value } |
release(options) |
释放 SDK(断开全部连接、注销监听),可再次 init |
success |
destroy(options) |
销毁 SDK 实例,可重新 init 并按新配置构建 |
success |
setDebugMode(options) |
开关底层调试日志 | success:{ value } |
requestPermissions(options) |
申请运行时权限(缺省:扫描 / 连接 / 定位) | success:ClassicPermissionResult |
checkPermissions(options) |
检查权限(不弹框) | success:ClassicPermissionResult |
getBluetoothAdapter(options) |
本机蓝牙适配器快照 | success:{ name, address, state, enabled, discovering } |
isBluetoothOn(options) |
蓝牙是否已开启 | success:{ value } |
getPairedDevices(options) |
系统已配对设备列表 | success:{ list } |
startDiscovery(options) |
开始搜索,结果走 onDeviceFound 事件 |
success / fail |
stopDiscovery(options) |
停止搜索 | success |
isDiscovering(options) |
是否正在搜索 | success:{ value } |
registerObserver(options) |
重新注册事件观察者(init 时已自动注册) |
success |
unregisterObserver(options) |
取消事件观察者(连接与数据仍在底层运行) | success |
isObserverRegistered(options) |
观察者是否已注册 | success:{ value } |
createConnection(options) |
创建连接实例(不发起连接) | success:ClassicConnection |
connect(options) |
发起连接,成功后才回调 success |
success:ClassicConnection / fail |
disconnectConnection(options) |
断开指定连接(可重连) | success |
disconnectAllConnections(options) |
断开全部连接 | success |
releaseConnection(options) |
释放指定连接(不可重连) | success |
releaseAllConnections(options) |
释放全部连接 | success |
getConnection(options) |
查询指定连接 | success:ClassicConnection |
getConnections(options) |
全部连接(无序) | success:{ list } |
getOrderedConnections(options) |
全部连接(按创建顺序) | success:{ list } |
getFirstConnection(options) |
第一个创建的连接 | success:ClassicConnection |
getLastConnection(options) |
最后一个创建的连接 | success:ClassicConnection |
clearQueue(options) |
清空指定连接的待写队列 | success |
write(options) |
写入数据(hex / base64 / text 三选一) |
success:ClassicWriteResult / onWrite(viaObserver) |
getBondState(options) |
查询配对状态 | success:{ address, bondState } |
createBond(options) |
发起配对 | success / fail,配对结果走 onBondStateChanged 事件 |
removeBond(options) |
解除配对 | success / fail,解除结果走 onBondStateChanged 事件 |
clearBondDevices(options) |
批量解除配对(不传 addresses 表示全部) |
success / fail,结果走 onBondStateChanged 事件 |
连接对象(ClassicConnection)
{
address: '00:11:22:33:44:55', // 设备 MAC
name: 'HC-05', // 设备名称,无权限时为空字符串
uuid: '00001101-...', // 该连接使用的服务 UUID
state: 4, // 0 未连接 | 1 连接中 | 2 配对中 | 3 已配对 | 4 已连接 | 5 已释放
connected: true, // 是否已连接(state == 4)
released: false, // 是否已释放(released 后不可重连)
bondState: 12 // 配对状态:10 未配对 | 11 配对中 | 12 已配对
}
事件对象(ClassicEvent)
所有字段始终存在,不属于当前事件的字段为空字符串 / 0 / false。
{
type: 'onRead', // onBluetoothAdapterStateChanged | onRead | onWrite | onConnectionStateChanged |
// onBondStateChanged | onDiscoveryStart | onDiscoveryStop | onDeviceFound | onDiscoveryError
address: '00:11:...', // 设备 MAC
name: 'HC-05', // 设备名称
uuid: '00001101-...', // 服务 UUID(连接 / 数据事件)
state: 4, // 连接状态(onConnectionStateChanged)
adapterState: 12, // 适配器状态(onBluetoothAdapterStateChanged)
bondState: 12, // 配对状态(设备相关事件)
deviceType: 1, // 0 未知 | 1 经典 | 2 低功耗 | 3 双模
rssi: -60, // 信号强度(onDeviceFound,单位 dBm)
tag: 'req1', // 写入标识(onWrite)
hex: '0102FF', // 十六进制(onRead / onWrite,大写无分隔符)
text: '...', // UTF-8 文本(onRead / onWrite,二进制可能乱码)
base64: '...', // Base64(onRead)
length: 3, // 字节长度(onRead / onWrite)
success: true, // 写入是否成功(onWrite)
discoveryErrorCode: 0, // 搜索错误码(onDiscoveryError):0 缺定位权限 | 1 位置服务未开启 | 2 搜索失败 |
// 3 缺扫描权限 | 4 缺连接权限
message: '...', // 错误描述(onDiscoveryError)
timestamp: 1762300000000 // 事件时间戳(毫秒)
}
写入结果(ClassicWriteResult)
{
address: '00:11:22:33:44:55', // 设备 MAC
uuid: '00001101-...', // 服务 UUID
tag: 'req1', // 写入标识(未传 tag 时由插件生成)
hex: '0102FF', // 实际写入的十六进制
length: 3, // 字节数
cost: 12, // 从入队到写完耗时(毫秒)
errMsg: '写入成功'
}
权限结果(ClassicPermissionResult)
{
list: [{ name: 'android.permission.BLUETOOTH_SCAN', granted: true }],
allGranted: true, // 是否全部授予
permanentlyDenied: [] // 被永久拒绝(勾选过「不再询问」),需引导用户去系统设置
}
配对状态(bondState)
bondState 取自 Android 系统配对状态常量(BluetoothDevice.BOND_*),插件原样透传,不做二次映射:
| 值 | 常量 | 含义 |
|---|---|---|
| 10 | BOND_NONE |
未配对(从未配对,或已解除配对) |
| 11 | BOND_BONDING |
配对中(系统正在配对,可能正在弹配对码 / 确认框) |
| 12 | BOND_BONDED |
已配对(系统已保存密钥,可以直接发起 SPP 连接) |
出现位置(四处含义一致):getBondState 返回的 bondState、getPairedDevices 返回的设备项、onDeviceFound 事件,以及 onBondStateChanged 事件(配对状态变化时主动推送)。
配对结果请统一监听 onBondStateChanged:createBond / removeBond / clearBondDevices 发起后,系统会通过该事件回传 11(配对中)、12(已配对)或 10(解除配对);在系统设置里手动配对/解除配对同样会收到。onConnectionStateChanged 的 2(配对中)/ 3(已配对)只在该设备已存在连接对象时才会出现,不能作为唯一的配对结果来源。
getBondState({
address: '00:11:22:33:44:55',
success: (res) => {
if (res.bondState === 12) {
// 已配对,可直接 connect
} else if (res.bondState === 11) {
// 配对中:等待系统弹框确认,结果看 onBondStateChanged 的 bondState
} else {
// 未配对:先 createBond
}
}
})
注意区分两套取值:
bondState是系统配对状态(10 / 11 / 12),state是插件连接状态(0 未连接 / 1 连接中 / 2 配对中 / 3 已配对 / 4 已连接 / 5 已释放),不要混用。
错误码
| 错误码 | 说明 |
|---|---|
| 9270001 | 参数错误 |
| 9270002 | 未初始化,请先调用 init |
| 9270003 | 设备不支持蓝牙 |
| 9270004 | 蓝牙未开启,请先开启蓝牙 |
| 9270005 | 缺少蓝牙扫描权限,请先调用 requestPermissions 申请 |
| 9270006 | 缺少蓝牙连接权限,请先调用 requestPermissions 申请 |
| 9270007 | 缺少定位权限或系统位置服务未开启,搜索设备需要定位权限 |
| 9270008 | 蓝牙地址非法或设备不存在 |
| 9270009 | 连接失败 |
| 9270010 | 连接已存在或已连接 |
| 9270011 | 未连接或连接已断开 |
| 9270012 | 连接已释放,请重新 connect |
| 9270013 | 写入失败 |
| 9270014 | 获取上下文失败 |
| 9270015 | UUID 格式非法 |
| 9270016 | 配对失败 |
| 9270017 | 取消配对失败 |
| 9270018 | 搜索失败 |
| 9270019 | 连接超时 |
| 9270020 | 操作失败 |
权限说明
| 权限 | 用途 |
|---|---|
android.permission.BLUETOOTH |
经典蓝牙基础权限(Android 11 及以下) |
android.permission.BLUETOOTH_ADMIN |
搜索、配对等管理操作(Android 11 及以下) |
android.permission.BLUETOOTH_SCAN |
搜索附近蓝牙设备(Android 12 及以上) |
android.permission.BLUETOOTH_CONNECT |
连接设备、读取设备名称与适配器信息(Android 12 及以上) |
android.permission.ACCESS_FINE_LOCATION |
搜索蓝牙设备所需定位权限(插件按 targetSdkVersion ≥ 29 校验,Android 12 及以上同样需要) |
android.permission.ACCESS_COARSE_LOCATION |
模糊定位权限,Android 11 及以下与精确定位配合使用 |
android.permission.INTERNET |
由依赖库声明合并引入,本插件业务不使用 |
android.permission.ACCESS_NETWORK_STATE |
由依赖库声明合并引入,本插件业务不使用 |
android.permission.ACCESS_WIFI_STATE |
由依赖库声明合并引入,本插件业务不使用 |
android.permission.CHANGE_WIFI_STATE |
由依赖库声明合并引入,本插件业务不使用 |
注意:搜索设备时插件会同时校验「蓝牙开关 → 扫描权限 → 连接权限 → 精确定位权限 → 系统位置服务」。Android 12 及以上如果不授予定位权限,
startDiscovery会返回9270007。

收藏人数:
购买源码授权版(
试用
使用 HBuilderX 导入示例项目
赞赏(0)
下载 66
赞赏 0
下载 12747329
赞赏 1959
赞赏
京公网安备:11010802035340号