更新记录

1.0.0(2026-10-31)

初始版本

  • 安卓经典蓝牙(经典蓝牙 / SPP)操作完整能力,统一为 zy-using-bluetooth UTS 插件
  • 权限:申请插件所需运行时权限、检查「所有文件」访问权限、跳转「所有文件」访问权限页
  • 基础能力:初始化(注册事件监听)、释放资源
  • 蓝牙开关:检查是否支持蓝牙、检查是否已开启、开启蓝牙、关闭蓝牙、跳转蓝牙设置界面
  • 搜索与配对:获取已配对设备、搜索设备、停止搜索、设备配对、取消配对
  • 连接:连接设备、断开连接、检查是否已连接、获取当前连接的设备
  • 发送数据:发送字节数据、发送十六进制字符串、发送文本数据、发送文件、选择文件并发送
  • 文件接收:收到传输请求后确认或拒绝接收,接收进度与完成结果持续回调
  • 事件:开关状态、发现设备、搜索开始/结束、配对过程、连接状态、数据收发、文件传输进度、内部错误

平台兼容性

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 × × ×

插件介绍

安卓经典蓝牙(Bluetooth Classic / SPP)操作 UTS 原生插件。一个插件覆盖蓝牙开关、设备搜索与配对、SPP 连接、数据收发(字节 / 十六进制 / 文本 / 文件)与文件接收确认,并提供运行时权限申请与「所有文件」访问权限检查。

支持 uni-app 与 uni-app x 不依赖任何三方库。

使用场景

  • 连接蓝牙打印机、蓝牙电子秤、蓝牙读卡器等 SPP 串口类外设,收发指令与数据
  • 与另一台手机 / 工位机做点对点文件互传(发送方发起请求,接收方确认后接收)
  • 需要先引导用户开启蓝牙、授权蓝牙扫描权限的设备联调场景
  • 需要把接收到的文件落到本机目录、再交给业务处理的场景

使用流程

  1. 申请权限:requestPermission() 申请蓝牙扫描 / 连接(Android 12+)或定位(Android 6~11)权限;接收文件到公共目录还需 checkAllFilesPermission() + toAllFilesPermissionPage() 引导授权。
  2. 初始化:init({ onEvent }) 注册事件回调,之后所有状态变化都从 onEvent 回调(type 区分事件)。
  3. 检查并开启蓝牙:isBluetoothSupported() → isBluetoothEnabled() → enableBluetooth()(弹系统授权框)。
  4. 搜索与配对:getPairedDevices() 或 startDiscovery()(设备从 onDeviceFound 事件取),stopDiscovery() 停止;必要时 pairDevice({ address }) 配对。两台手机互连时,接收方先 enableDiscoverable() 才能被对方搜到。
  5. 连接:connect({ address }),连接结果以 onConnectionStateChanged 事件的 state == 3 为准。
  6. 收发数据:sendText / sendHexString / sendBytes / sendFile / selectFileAndSend;收到的数据从 onDataReceived 事件取;对端发文件时在 onFileReceiveRequest 里调用 confirmFileReceive({ accept }) 决定收不收。
  7. 释放:disconnect() 断开,release() 释放全部资源(建议放在页面 onUnload)。

用法

import {
    requestPermission,
    checkAllFilesPermission,
    toAllFilesPermissionPage,
    init,
    release,
    isBluetoothSupported,
    isBluetoothEnabled,
    enableBluetooth,
    disableBluetooth,
    openBluetoothSettings,
    getPairedDevices,
    startDiscovery,
    stopDiscovery,
    pairDevice,
    unpairDevice,
    connect,
    disconnect,
    isConnected,
    getConnectedDevice,
    sendBytes,
    sendHexString,
    sendText,
    sendFile,
    selectFileAndSend,
    confirmFileReceive
} from '@/uni_modules/zy-using-bluetooth'

权限申请

requestPermission({
    success: (res) => {
        // res.list:[{ name: 'android.permission.BLUETOOTH_CONNECT', granted: true }]
        // res.allGranted:是否全部授予
        // res.permanentlyDenied:被永久拒绝的权限名(需引导去设置页手动开启)
        console.log('全部授予:' + res.allGranted)
    },
    fail: (err) => console.log('失败 ' + err.errCode + ' ' + err.errMsg)
})

// 指定权限列表(缺省为插件所需的全部运行时权限)
requestPermission({ permissions: ['android.permission.BLUETOOTH_SCAN'] })

所有文件访问权限

checkAllFilesPermission({
    success: (res) => {
        // res.result:true 已授权 / false 未授权
        console.log('所有文件权限:' + res.result)
    }
})

// Android 11 及以上跳系统「所有文件访问权限」页;以下跳应用详情页
toAllFilesPermissionPage({
    success: () => console.log('已跳转,返回后可重新检查')
})

初始化与释放

init({
    onEvent: (event) => {
        // event.type 取值见「事件结构」;所有事件都在主线程回调
        console.log('事件:' + event.type)
        if (event.type === 'onBluetoothStateChanged') {
            console.log('蓝牙是否开启:' + event.isEnabled)
        } else if (event.type === 'onDeviceFound') {
            console.log(event.deviceName + ' ' + event.deviceAddress + ' RSSI ' + event.deviceRssi)
        } else if (event.type === 'onConnectionStateChanged') {
            // state:0 未连接 | 1 监听中 | 2 连接中 | 3 已连接
            console.log('连接状态:' + event.state)
        } else if (event.type === 'onDataReceived') {
            // data:按 UTF-8 解码的文本;hex:大写十六进制;length:字节数
            console.log('收到 ' + event.length + ' 字节:' + event.hex)
        } else if (event.type === 'onFileReceiveRequest') {
            // 收到对端的文件传输请求,由业务决定是否接收
            confirmFileReceive({ accept: true })
        }
    },
    success: () => console.log('初始化成功'),
    fail: (err) => console.log('初始化失败 ' + err.errCode + ' ' + err.errMsg)
})

蓝牙开关

isBluetoothSupported({ success: (res) => console.log('支持蓝牙:' + res.result) }) // 不需要 init
isBluetoothEnabled({ success: (res) => console.log('蓝牙已开启:' + res.result) })   // 不需要 init

enableBluetooth({ success: () => console.log('已弹系统开启框') })  // 结果以 onBluetoothStateChanged 为准
disableBluetooth({ success: (res) => console.log('关闭结果:' + res.result) })
openBluetoothSettings({ success: () => console.log('已跳转蓝牙设置') })

搜索与配对

getPairedDevices({
    success: (res) => {
        // res.list:[{ name, address, bondState }],bondState:10 未配对 | 11 配对中 | 12 已配对
        console.log('已配对 ' + res.list.length + ' 个')
    }
})

startDiscovery({ success: () => console.log('开始搜索') })
stopDiscovery({ success: () => console.log('停止搜索') })

pairDevice({
    address: '00:11:22:33:44:55',
    success: () => console.log('配对请求已发起'),
    fail: (err) => console.log('配对失败 ' + err.errCode + ' ' + err.errMsg)
})

unpairDevice({ address: '00:11:22:33:44:55' })

连接

connect({
    address: '00:11:22:33:44:55',
    success: () => console.log('连接请求已发起,等待 onConnectionStateChanged'),
    fail: (err) => console.log('连接失败 ' + err.errCode + ' ' + err.errMsg)
})

isConnected({ success: (res) => console.log('已连接:' + res.result) })
getConnectedDevice({ success: (res) => console.log(res.device.name + ' ' + res.device.address) })
disconnect()

发送数据

// 字节数组,元素取值 0~255
sendBytes({ data: [0x01, 0x02, 0x0A] })

// 也可以直接传十六进制 / base64 字符串(大包推荐,省掉 number[] 的 JSON 序列化开销)
sendBytes({ hex: '01 02 0A' })
sendBytes({ base64: 'AQIK' })

// 十六进制字符串(空格会被忽略,长度必须为偶数)
sendHexString({ data: '01 02 0A' })

// 文本,按 UTF-8 编码
sendText({ data: 'hello bluetooth' })

// 发送文件:先发传输请求,对端确认后开始发送(30 秒未确认则失败)
sendFile({ path: '/storage/emulated/0/test.txt' })

// 打开系统文件选择器,选中后自动发送(文件会先复制到应用私有目录)
selectFileAndSend({
    success: (res) => console.log('开始发送 ' + res.fileName + '(' + res.fileSize + ' 字节)')
})

接收文件

// 收到 onFileReceiveRequest 后决定
confirmFileReceive({
    accept: true, // false 为拒绝,已接收的部分会被删除
    saveDir: '/storage/emulated/0/Download', // 可选,缺省为应用私有 Download 目录
    success: () => console.log('已回复对端')
})

接收目录不存在会自动创建;不可写时回退到应用私有目录并回调 onError(9250014)。 同名文件已存在时自动追加 (1)(2)…,不会覆盖已有文件。

本机可被发现(两台手机互连)

// 让对方能搜到本机并发起配对,duration 缺省 120 秒
enableDiscoverable({
    duration: 120,
    success: () => console.log('已弹系统「允许被搜索」框')
})

// 结果看事件
// onScanModeChanged -> { scanMode: 23, discoverable: true }

插件方法

方法 说明 回调
requestPermission(options?) 申请插件所需运行时权限,options.permissions 可覆盖缺省列表 成功 list / allGranted / permanentlyDenied
checkAllFilesPermission(options?) 检查「所有文件」访问权限 成功 result(布尔)
toAllFilesPermissionPage(options?) 跳转「所有文件」访问权限页 成功 errMsg
init(options?) 初始化并注册事件回调,必须先调用 成功 errMsg;事件走 onEvent
release(options?) 断开连接、反注册广播、清空状态 成功 errMsg
isBluetoothSupported(options?) 检查设备是否支持蓝牙(可不 init) 成功 result(布尔)
isBluetoothEnabled(options?) 检查蓝牙是否已启用(可不 init) 成功 result(布尔)
enableBluetooth(options?) 弹系统授权框开启蓝牙 成功 errMsg;结果以 onBluetoothStateChanged 为准
disableBluetooth(options?) 关闭蓝牙(已开启时才能调用) 成功 result(布尔)
openBluetoothSettings(options?) 跳转系统蓝牙设置页 成功 errMsg
enableDiscoverable(options?) 让本机可被其他设备搜索到,options.duration 为时长(秒,缺省 120) 成功 errMsg;结果以 onScanModeChanged 为准
getPairedDevices(options?) 获取已配对设备 成功 list
startDiscovery(options?) 搜索设备(连接中 / 已连接时不可用) 成功 errMsg;设备走 onDeviceFound
stopDiscovery(options?) 停止搜索 成功 errMsg
pairDevice(options) 设备配对,options.address 必传(已搜索/已配对的设备直接用实例,否则按 MAC 构造) 成功 errMsg;过程走 onPairingRequest / onPairingCompleted
unpairDevice(options) 取消配对,options.address 必传 成功 errMsg
connect(options) 连接设备(SPP,UUID 00001101-0000-1000-8000-00805F9B34FB),options.address 必传 成功 errMsg;结果走 onConnectionStateChanged
disconnect(options?) 断开连接 成功 errMsg
isConnected(options?) 检查是否已连接 成功 result(布尔)
getConnectedDevice(options?) 获取当前连接的设备(未连接返回 9250006) 成功 device
sendBytes(options) 发送字节数据,data(0~255 数组)/ hex / base64 三选一(优先级 hex > base64 > data) 成功 errMsg
sendHexString(options) 发送十六进制字符串,options.data 成功 errMsg
sendText(options) 发送文本(UTF-8),options.data 成功 errMsg
sendFile(options) 发送文件,options.path 为绝对路径 成功 errMsg;进度走 onFileSendProgress / onFileSendComplete
selectFileAndSend(options?) 打开系统文件选择器并发送 成功 fileName / filePath / fileSize
confirmFileReceive(options) 确认或拒绝接收文件,options.accept、options.saveDir(可选,接收目录) 成功 errMsg

回调结果结构

// 通用失败对象(UniError)
{
    errSubject : 'zy-using-bluetooth',
    errCode : 9250006,          // 见「错误码」
    errMsg : '蓝牙未连接'        // 错误描述
}

// 设备对象(getPairedDevices 的 list 元素、getConnectedDevice 的 device)
{
    name : 'BT-Printer',              // 设备名称,未取到为空字符串
    address : '00:11:22:33:44:55',    // MAC 地址
    bondState : 12                    // 10 未配对 | 11 配对中 | 12 已配对
}

// selectFileAndSend 成功结果
{
    fileName : 'test.txt',                              // 文件名
    filePath : '/data/user/0/<包名>/cache/test.txt',      // 复制到私有目录后的路径
    fileSize : 1024,                                    // 字节数
    errMsg : '文件已选择,开始发送'
}

事件结构(init 的 onEvent)

{
    type : 'onDeviceFound',   // 事件类型,见下表
    deviceName : 'BT-Printer',        // 设备名称(设备相关事件)
    deviceAddress : '00:11:22:33:44:55',
    deviceBondState : 12,             // 10 未配对 | 11 配对中 | 12 已配对
    deviceRssi : -63,                 // 信号强度(dBm,通常为负值),onDeviceFound 时有效
    isEnabled : true,                 // 蓝牙是否开启,onBluetoothStateChanged / onInit 时有效
    state : 3,                        // 0 未连接 | 1 监听中 | 2 连接中 | 3 已连接,onConnectionStateChanged 时有效
    success : true,                   // 配对/发送是否成功,onPairingCompleted / onFileSendComplete / onFileReceiveComplete 时有效
    data : 'hello',                   // 数据文本(UTF-8 解码),onDataReceived / onDataSent 时有效
    hex : '68656C6C6F',               // 数据十六进制(大写、无分隔符)
    length : 5,                       // 数据字节数
    fileName : 'test.txt',            // 文件名(文件相关事件)
    totalSize : 1024,                 // 文件总大小(字节,发送/接收进度事件)
    sentSize : 512,                   // 已发送字节数,onFileSendProgress 时有效
    receivedSize : 512,               // 已接收字节数,onFileReceiveProgress 时有效
    fileSize : 1024,                  // 待传输文件大小,onFileReceiveRequest 时有效
    filePath : '/storage/emulated/0/Android/data/<包名>/files/Download/test.txt', // 接收完成的文件路径
    confirmed : true,                 // 对端是否同意接收,onFileReceiveConfirm 时有效
    errCode : 0,                      // 错误码(9250xxx),onError 时有效
    errorMessage : '',                // 错误描述,onError 时有效
    scanMode : 23,                    // 20 不可被发现 | 21 可被连接 | 23 可被连接且可被发现,onScanModeChanged 时有效
    discoverable : true               // 本机是否可被搜索到,onScanModeChanged 时有效
}

事件类型:

type 触发时机 关键字段
onInit 初始化完成 isEnabled
onBluetoothStateChanged 蓝牙开关状态变化 isEnabled
onDeviceFound 搜索到一个设备 deviceName / deviceAddress / deviceBondState / deviceRssi
onDiscoveryStarted 开始搜索 -
onDiscoveryStopped 搜索结束(搜索超时或被停止) -
onPairingRequest 开始配对 deviceName / deviceAddress
onPairingCompleted 配对结束 success / deviceAddress
onConnectionStateChanged 连接状态变化 state / deviceAddress
onDataReceived 收到对端数据 data / hex / length
onDataSent 本端数据发送成功 data / hex / length
onFileSendProgress 文件发送进度(约每 8KB 一次,间隔 ≥ 5 毫秒) fileName / sentSize / totalSize
onFileSendComplete 文件发送结束 fileName / success
onFileReceiveRequest 收到对端文件传输请求 fileName / fileSize / deviceName
onFileReceiveProgress 文件接收进度(每 100 毫秒最多一次) fileName / receivedSize / totalSize
onFileReceiveComplete 文件接收结束 fileName / filePath / success
onFileReceiveConfirm 对端回复确认/拒绝 fileName / confirmed
onScanModeChanged 本机可被发现状态变化 scanMode / discoverable
onError 内部错误(连接断开、写入失败、文件传输失败等) errCode / errorMessage

错误码

错误码 说明
9250001 参数错误
9250002 未初始化,请先调用 init
9250003 设备不支持蓝牙
9250004 蓝牙未开启
9250005 蓝牙已开启
9250006 蓝牙未连接
9250007 文件不存在
9250008 缺少蓝牙扫描权限,请先调用 requestPermission 申请
9250009 缺少蓝牙连接权限,请先调用 requestPermission 申请
9250010 缺少位置权限,搜索设备需要位置权限
9250011 连接失败
9250012 未找到蓝牙设备,请先搜索或获取已配对设备
9250013 获取上下文失败
9250014 文件传输失败
9250015 操作失败

权限说明

权限 用途
android.permission.BLUETOOTH Android 11 及以下:蓝牙基础操作(开启、搜索、连接)
android.permission.BLUETOOTH_ADMIN Android 11 及以下:开启 / 关闭蓝牙、发起搜索与配对
android.permission.BLUETOOTH_CONNECT Android 12 及以上:查询已配对设备、连接、配对、收发数据(运行时权限)
android.permission.BLUETOOTH_SCAN Android 12 及以上:搜索附近蓝牙设备(运行时权限)
android.permission.ACCESS_FINE_LOCATION Android 11 及以下:搜索蓝牙设备必需的定位权限(运行时权限)
android.permission.ACCESS_COARSE_LOCATION Android 11 及以下:辅助定位权限,与精确定位一起申请
android.permission.READ_EXTERNAL_STORAGE Android 10 及以下:读取待发送文件
android.permission.WRITE_EXTERNAL_STORAGE Android 10 及以下:写入接收到的文件
android.permission.MANAGE_EXTERNAL_STORAGE Android 11 及以上:「所有文件」访问权限(特殊权限,需跳转设置页由用户手动授予)

隐私、权限声明

1. 本插件需要申请的系统权限列表:

android.permission.BLUETOOTH android.permission.BLUETOOTH_ADMIN android.permission.BLUETOOTH_CONNECT android.permission.BLUETOOTH_SCAN android.permission.ACCESS_FINE_LOCATION android.permission.ACCESS_COARSE_LOCATION android.permission.READ_EXTERNAL_STORAGE android.permission.WRITE_EXTERNAL_STORAGE android.permission.MANAGE_EXTERNAL_STORAGE

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

插件仅在本机进行蓝牙搜索、配对与数据传输,采集到的设备与数据不会上传至任何服务器

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

无

暂无用户评论。