@fww_123/uni-ble-scanner
v2.0.0
Published
跨平台蓝牙低功耗扫描组件,支持安卓、鸿蒙、iOS
Maintainers
Readme
@fww_123/uni-ble-scanner
跨平台蓝牙低功耗(BLE)扫描组件,一套代码支持 Android、iOS、鸿蒙 三大原生平台,以及 UniApp、APICloud、H5 WebView 等多种开发框架。
📖 目录
✨ 特性
- 🚀 跨框架:支持 UniApp、APICloud、H5(WebView)、纯 H5
- 📱 真原生:直接调用 Android/iOS/鸿蒙原生蓝牙 API,非 JS 模拟
- 🔧 统一接口:所有框架使用相同的 JavaScript API
- � 自动降级:原生插件不可用时自动切换到标准 API
- 🛡️ TypeScript:完整的类型定义和代码提示
- 📦 开箱即用:npm 安装,引入即用
📦 安装
npm install @fww_123/uni-ble-scanner🚀 快速开始
最简单的使用方式(推荐)
适用于:UniApp、APICloud、H5 + WebView、纯 H5
<!-- 1. 引入 Bridge 文件 -->
<script src="node_modules/@fww_123/uni-ble-scanner/native-plugins/bridge/universal-bridge.js"></script>
<script>
// 2. 初始化蓝牙
BLEScanner.init({}, function(ret, err) {
if (err) {
console.error('初始化失败:', err.message);
return;
}
console.log('初始化成功:', ret.platform);
});
// 3. 开始扫描
BLEScanner.startScan({
timeout: 10000, // 扫描 10 秒
services: [] // 可选:指定要扫描的服务 UUID
}, function(ret, err) {
if (err) {
console.error('扫描错误:', err.message);
return;
}
// 处理扫描事件
switch(ret.event) {
case 'scanStart':
console.log('扫描开始');
break;
case 'deviceFound':
console.log('发现设备:', ret.device.name, ret.device.RSSI);
break;
case 'scanStop':
console.log('扫描停止');
break;
case 'timeout':
console.log('扫描超时');
break;
}
});
</script>TypeScript / ES Module 方式
import { createScanner, createNativeScanner } from '@fww_123/uni-ble-scanner';
// 方式1:自动检测平台
const scanner = createScanner({ type: 'auto' });
// 方式2:强制使用原生
const scanner = createNativeScanner();
// 初始化
await scanner.init();
// 开始扫描
await scanner.startScan({
timeout: 30000,
allowDuplicates: false
}, {
onDeviceFound: (device) => {
console.log('发现设备:', device.name, device.RSSI);
},
onScanStart: () => console.log('扫描开始'),
onScanStop: () => console.log('扫描停止')
});🏗️ 架构说明
整体架构
┌─────────────────────────────────────────────────────────────┐
│ 你的 APP(任意框架) │
│ UniApp / APICloud / H5+原生壳 / 纯 H5 │
└───────────────────────────┬─────────────────────────────────┘
│
┌─────────────▼──────────────┐
│ universal-bridge.js │
│ ├─ 自动检测运行环境 │
│ ├─ 统一 API 封装 │
│ └─ 自动降级处理 │
└─────────────┬──────────────┘
│
┌───────────────────┼───────────────────┐
│ │ │
┌───────▼───────┐ ┌────────▼────────┐ ┌──────▼──────┐
│ Android │ │ iOS │ │ 鸿蒙 │
│ Java 原生 │ │ OC 原生 │ │ ArkTS 原生 │
│ │ │ │ │ │
│ BluetoothLe │ │ CoreBluetooth │ │ @ohos. │
│ Scanner │ │ │ │ bluetooth │
└───────────────┘ └─────────────────┘ └─────────────┘工作原理
- 环境检测:
universal-bridge.js自动检测当前运行环境 - 适配层:根据环境选择最佳的实现方式
- 有原生插件 → 使用原生插件(性能最好)
- 无原生插件 → 使用框架标准 API
- H5 环境 → 使用 Web Bluetooth API
- 统一接口:无论底层如何,对外提供一致的 API
各平台实现
| 平台 | 技术方案 | 原生 API | 最低版本 |
|------|---------|---------|---------|
| Android | UniApp 原生插件 / Native.js | BluetoothLeScanner | Android 5.0+ |
| iOS | UniApp 原生插件 / Native.js | CoreBluetooth | iOS 7.0+ |
| 鸿蒙 | 原生 ArkTS 模块 | @ohos.bluetooth.ble | HarmonyOS 2.0+ |
| Web | Web Bluetooth API | navigator.bluetooth | Chrome/Edge |
📱 框架集成
UniApp
方案 A:使用原生插件(推荐,性能最佳)
- 复制原生插件到项目
# Android
mkdir -p nativeplugins/ble-scanner/android
mkdir -p nativeplugins/ble-scanner/ios
# 复制插件文件
cp -r node_modules/@fww_123/uni-ble-scanner/native-plugins/android/ble-scanner/* nativeplugins/ble-scanner/android/
cp -r node_modules/@fww_123/uni-ble-scanner/native-plugins/ios/ble-scanner/* nativeplugins/ble-scanner/ios/- 配置 manifest.json
{
"nativePlugins": {
"ble-scanner": {
"android": {
"package": "com.blescanner",
"class": "BLEScannerModule"
},
"ios": {
"class": "BLEScannerModule"
}
}
}
}- 在页面中引入 Bridge
<template>
<view>
<!-- 你的页面内容 -->
</view>
</template>
<script>
// 方式1:直接引入 Bridge(推荐)
// 在 index.html 的 head 中添加:
// <script src="static/js/universal-bridge.js"><\/script>
// 方式2:使用 npm 包
import { createScanner } from '@fww_123/uni-ble-scanner';
export default {
methods: {
async initBLE() {
// 使用全局 BLEScanner 对象
BLEScanner.init({}, (ret, err) => {
if (ret) console.log('成功');
});
// 或使用 npm 包
const scanner = createScanner({ type: 'native' });
await scanner.init();
}
}
}
</script>方案 B:使用标准 API(无需配置原生插件)
直接使用 universal-bridge.js,它会自动使用 UniApp 标准蓝牙 API。
<script src="path/to/universal-bridge.js"></script>
<script>
// 自动降级到 uni.openBluetoothAdapter
BLEScanner.init({}, callback);
</script>APICloud
导入模块
- 将 Android/iOS 模块编译后导入 APICloud 项目
- 在
config.xml中添加模块配置
使用 Bridge
<!DOCTYPE html>
<html>
<head>
<script src="path/to/universal-bridge.js"></script>
</head>
<body>
<script>
apiready = function() {
BLEScanner.init({}, function(ret, err) {
// 初始化完成
});
};
</script>
</body>
</html>H5 + 原生 WebView
如果你的 APP 是原生开发,内嵌 H5 页面:
Android 端(Java/Kotlin):
// 在 WebView 中注入 Bridge
webView.getSettings().setJavaScriptEnabled(true);
webView.addJavascriptInterface(new BLEScannerBridge(this), "BLEScannerNative");
// BLEScannerBridge.java
public class BLEScannerBridge {
@JavascriptInterface
public void init(String options, String callbackId) {
// 调用原生蓝牙 API
}
}iOS 端(Objective-C/Swift):
// 使用 WebViewJavascriptBridge
[bridge registerHandler:@"BLEScanner.init" handler:^(id data, WVJBResponseCallback responseCallback) {
// 初始化蓝牙
responseCallback(@{@"status": @YES});
}];
[bridge registerHandler:@"BLEScanner.startScan" handler:^(id data, WVJBResponseCallback responseCallback) {
// 开始扫描
// 扫描到设备后通过 callback 返回
}];H5 页面:
<script src="path/to/universal-bridge.js"></script>
<script>
// universal-bridge.js 会自动检测 WebViewJavascriptBridge
BLEScanner.init({}, function(ret, err) {
console.log('初始化结果:', ret);
});
</script>纯 H5(Web Bluetooth)
<script src="path/to/universal-bridge.js"></script>
<script>
// 仅支持 Chrome、Edge、Opera 等浏览器
// 必须在 HTTPS 环境下运行
BLEScanner.init({}, function(ret, err) {
if (err) {
alert('浏览器不支持蓝牙或权限被拒绝');
return;
}
BLEScanner.startScan({}, function(ret, err) {
if (ret.event === 'deviceFound') {
console.log(ret.device);
}
});
});
</script>� API 文档
统一 API(Bridge 模式)
BLEScanner.init(options, callback)
初始化蓝牙适配器。
参数:
options{Object}- 配置选项(可选)restoreIdentifier{string}- iOS 后台恢复标识
callback{Function}- 回调函数callback(ret, err)
回调结果:
// 成功
{
status: true,
platform: 'android' // 'android' | 'ios' | 'harmony' | 'web'
}
// 失败
{
code: -1, // 错误码
message: '错误信息' // 错误描述
}错误码说明: | 错误码 | 说明 | |--------|------| | -1 | 缺少权限 | | -2 | 设备不支持蓝牙 | | -3 | 蓝牙未开启 | | -4 | 扫描已在进行中 | | -5 | 启动扫描失败 | | -10 | 用户取消 | | -99 | 其他错误 |
BLEScanner.startScan(options, callback)
开始扫描 BLE 设备。
参数:
options{Object}- 扫描选项timeout{number}- 扫描超时时间(毫秒),默认 10000services{string[]}- 要扫描的服务 UUID 数组allowDuplicates{boolean}- 是否允许重复设备,默认 falserssiThreshold{number}- RSSI 信号阈值(仅部分平台支持)
callback{Function}- 回调函数callback(ret, err)
回调事件:
// 扫描开始
{ event: 'scanStart' }
// 发现设备
{
event: 'deviceFound',
device: {
deviceId: 'xx:xx:xx:xx:xx:xx', // 设备 MAC 地址或 UUID
name: '设备名称', // 蓝牙设备名称
RSSI: -65, // 信号强度(dBm)
advertisData: '...', // 广播数据(十六进制字符串)
serviceUuids: ['...'] // 服务 UUID 列表
}
}
// 扫描停止
{ event: 'scanStop' }
// 扫描超时
{ event: 'timeout' }BLEScanner.stopScan(callback)
停止扫描。
参数:
callback{Function}- 回调函数callback(ret)
回调结果:
{ event: 'scanStop' }BLEScanner.checkPermission(callback)
检查蓝牙权限。
参数:
callback{Function}- 回调函数callback(ret)
回调结果:
{ granted: true } // true: 有权限,false: 无权限BLEScanner.requestPermission(callback)
请求蓝牙权限。
参数:
callback{Function}- 回调函数callback(ret)
回调结果:
{
requested: true,
message: '提示信息'
}BLEScanner.getDevices()
获取已发现的设备列表。
返回值:
[
{
deviceId: 'xx:xx:xx:xx:xx:xx',
name: '设备名称',
RSSI: -65
}
]TypeScript API
import {
createScanner,
createNativeScanner,
BLEDevice,
ScanOptions,
ScanCallbacks
} from '@fww_123/uni-ble-scanner';
// 创建设备
const scanner = createScanner({
type: 'auto' // 'auto' | 'uniapp' | 'native' | 'web'
});
// 类型定义
interface BLEDevice {
deviceId: string;
name?: string;
RSSI: number;
advertisData?: string;
serviceUuids?: string[];
}
interface ScanOptions {
timeout?: number;
services?: string[];
allowDuplicates?: boolean;
rssiThreshold?: number;
}
interface ScanCallbacks {
onDeviceFound?: (device: BLEDevice) => void;
onScanStart?: () => void;
onScanStop?: () => void;
onTimeout?: () => void;
onError?: (error: BLEError) => void;
}❓ 常见问题
Q1: 为什么扫描不到设备?
可能原因:
- 没有蓝牙权限 → 调用
BLEScanner.requestPermission() - 蓝牙未开启 → 引导用户开启蓝牙
- Android 6.0+ 需要位置权限 → 检查定位权限
- iOS 需要在真机上测试 → 模拟器不支持蓝牙
Q2: 原生插件和 Bridge 有什么区别?
| 特性 | 原生插件 | Bridge | |------|---------|--------| | 性能 | 最好 | 好 | | 配置 | 需要配置 manifest | 引入即用 | | 兼容性 | 需要编译 | 纯 JS,无需编译 | | 适用场景 | 生产环境 | 开发/快速集成 |
Q3: 如何在 UniApp 中判断使用原生还是标准 API?
// universal-bridge.js 会自动判断
// 你也可以手动指定
const scanner = createScanner({
type: 'native' // 强制使用原生
});
const scanner = createScanner({
type: 'uniapp' // 强制使用标准 API
});Q4: 支持后台扫描吗?
- Android: 支持,需要后台定位权限
- iOS: 支持,需要在
Info.plist中配置UIBackgroundModes - 鸿蒙: 支持
- Web: 不支持
Q5: 如何打包发布?
# 1. 安装依赖
npm install
# 2. 编译 TypeScript
npm run build
# 3. 发布到 npm
npm publish --access public� 目录结构
ble_test/
├── src/ # TypeScript 源码
│ ├── core/
│ │ └── base-scanner.ts # 扫描器基类
│ ├── platforms/
│ │ ├── android-native.ts # Android 原生实现
│ │ ├── ios-native.ts # iOS 原生实现
│ │ ├── harmony-native.ts # 鸿蒙原生实现
│ │ ├── uniapp-scanner.ts # UniApp 标准 API
│ │ └── web-scanner.ts # Web Bluetooth
│ ├── utils/
│ │ ├── platform.ts # 平台检测
│ │ ├── permissions.ts # 权限管理
│ │ ├── errors.ts # 错误处理
│ │ └── data-parser.ts # 数据解析
│ ├── factory.ts # 工厂函数
│ └── index.ts # 入口文件
├── native-plugins/ # 原生插件
│ ├── android/ # Android 原生模块
│ │ └── ble-scanner/ # UniApp/APICloud 模块
│ ├── ios/ # iOS 原生模块
│ │ └── ble-scanner/
│ ├── harmony/ # 鸿蒙原生模块
│ │ └── entry/src/main/ets/components/ble/
│ └── bridge/
│ └── universal-bridge.js # 跨框架 Bridge ⭐核心文件
├── examples/ # 示例代码
│ ├── cross-framework-demo/ # 多框架集成示例
│ │ ├── uniapp/ # UniApp 示例
│ │ ├── apicloud/ # APICloud 示例
│ │ └── h5-webview/ # H5 WebView 示例
│ ├── native-app-demo/ # 原生 APP 示例
│ └── web-demo/ # Web 示例
└── dist/ # 编译输出⚠️ 注意事项
Android
- 权限配置(
AndroidManifest.xml):
<!-- Android 12+ -->
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<!-- Android 11 及以下 -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<!-- 所有版本都需要 -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />- 动态权限申请(Android 6.0+):
- 需要动态申请位置和蓝牙权限
iOS
- Info.plist 配置:
<key>NSBluetoothAlwaysUsageDescription</key>
<string>需要使用蓝牙扫描设备</string>
<key>NSBluetoothPeripheralUsageDescription</key>
<string>需要使用蓝牙扫描设备</string>- 必须使用真机测试,模拟器不支持蓝牙
鸿蒙
- module.json5 配置:
{
"requestPermissions": [
{ "name": "ohos.permission.ACCESS_BLUETOOTH" },
{ "name": "ohos.permission.LOCATION" }
]
}Web
- 浏览器支持:仅 Chrome、Edge、Opera 支持 Web Bluetooth
- HTTPS:必须在 HTTPS 环境下使用
- 不支持后台扫描
� 支持与反馈
- 提交 Issue:GitHub Issues
- 邮件联系:[email protected]
📄 许可证
MIT License
Made with ❤️ for cross-platform BLE scanning
