1241 lines
34 KiB
Markdown
1241 lines
34 KiB
Markdown
# android-ble
|
||
|
||
`android-ble` 是一个面向 `uni-app` / `uni-app x` 的 BLE 低功耗蓝牙插件,支持 Android、iOS、鸿蒙、微信小程序。
|
||
|
||
插件市场相关资源:
|
||
|
||
- BLE 插件:`android-ble` 支付多端 安卓 ios 鸿蒙 微信小程序
|
||
- 经典蓝牙 SPP 插件:[安卓Spp经典蓝牙插件](https://ext.dcloud.net.cn/plugin?id=26830)
|
||
- Ble服务端插件[Android ble低功耗蓝牙周边服务蓝牙服务端](https://ext.dcloud.net.cn/plugin?id=22510)
|
||
- `uni-app` 示例工程压缩包:`bt_uniapp.zip`
|
||
- `uni-app x` 示例页面:`App.uvue`、`pages/Multi/Multi.uvue`
|
||
|
||
## 功能概览
|
||
|
||
- 支持 BLE 扫描、停止扫描、扫描结果过滤
|
||
- 支持连接、断开、自动重连、连接超时设置
|
||
- 支持扫描服务与特征值、自动识别可写/可通知 UUID
|
||
- 支持通知订阅、indicate 订阅、主动读取、RSSI 读取、MTU 设置
|
||
- 支持十六进制字符串、数字数组、`ArrayBuffer` 写入
|
||
- 支持分包发送
|
||
- 支持多蓝牙连接场景
|
||
- iOS 支持按服务 UUID 获取系统已连接设备,并接管到当前 `BleLib` 实例
|
||
- 支持监听系统蓝牙开关状态
|
||
|
||
## 平台支持
|
||
|
||
| 平台 | 支持情况 |
|
||
| --- | --- |
|
||
| uni-app Android | 支持 |
|
||
| uni-app iOS | 支持 |
|
||
| uni-app 鸿蒙 | 支持 |
|
||
| uni-app 微信小程序 | 支持 |
|
||
| uni-app x Android | 支持 |
|
||
| uni-app x iOS | 支持 |
|
||
| uni-app x 鸿蒙 | 支持 |
|
||
| uni-app x 微信小程序 | 支持 |
|
||
| Web | 不支持 |
|
||
|
||
补充说明:
|
||
|
||
- iOS 需 `iOS 13` 及以上版本。
|
||
- Android / iOS / 鸿蒙运行原生能力时,建议使用自定义基座验证。
|
||
- 微信小程序端按 `BleLib` 实例维护连接状态,适合一个对象对应一个蓝牙连接。
|
||
- `getConnectedDevicesWithServices` 与 `redictToConnect` 是 iOS 专属方法,只有 iOS 可以调用,Android、鸿蒙、微信小程序、Web 都不支持调用。
|
||
|
||
## 安装与运行
|
||
|
||
### 插件试用与下载
|
||
|
||
1. 在插件市场选择试用,并绑定目标项目 `appid`。
|
||
2. 下载插件到本地项目。
|
||
3. 在页面中从 `@/uni_modules/android-ble` 引入 `BleLib` 与相关类型。
|
||
|
||
## 快速上手
|
||
|
||
本节用于帮助开发者快速跑通 BLE 标准流程,下面给出 `uni-app x` 与 `uni-app` 双端示例。
|
||
|
||
### 最短接入步骤
|
||
|
||
1. 引入 `BleLib`,创建蓝牙对象实例。
|
||
2. 调用 `openBtBluetooth` 初始化蓝牙适配器。
|
||
3. 调用 `onStartScanBle` 开始扫描设备。
|
||
4. 拿到目标设备地址后调用 `connect`。
|
||
5. 连接成功后调用 `scanServices`。
|
||
6. 调用 `getUUIDs` 查看自动识别出的可用 UUID 组合。
|
||
7. 必要时调用 `setSelectUUID` 切换到目标 `service/notify/write` 组合。
|
||
8. 调用 `onNotityBleData` 开启通知,或调用 `onIndicate` 开启 indicate 订阅。
|
||
9. 调用 `sendData` 发送数据。
|
||
10. 结束时调用 `close` 断开连接。
|
||
|
||
### `uni-app x` 快速上手
|
||
|
||
```ts
|
||
import { BleLib, MyApiResult, BleScanResult, WNuuid, WriteData, NotityData, ScanPara } from "@/uni_modules/android-ble"
|
||
|
||
let lib = new BleLib()
|
||
let firstMac = ""
|
||
|
||
lib.openBtBluetooth(function(res:MyApiResult){
|
||
console.log("openBtBluetooth", res)
|
||
})
|
||
|
||
lib.onBtOpenStateListener(function(res:MyApiResult){
|
||
console.log("bt state", res)
|
||
})
|
||
|
||
let scanPara:ScanPara = {
|
||
scantime:10 * 1000,
|
||
showEmptyName:0,
|
||
btNameFilter:"",
|
||
onScanResult:function(res:MyApiResult){
|
||
// type 为0 扫描成功 10001 表示无蓝牙权限 10002 表示蓝牙未开启
|
||
if(res.type == 0){
|
||
let scan = res.data as BleScanResult
|
||
console.log("scan", scan)
|
||
if(firstMac == ""){
|
||
firstMac = scan.device.address
|
||
}
|
||
}
|
||
},
|
||
scanComplate:function(){
|
||
console.log("scan complete")
|
||
if(firstMac != ""){
|
||
lib.connect(firstMac,false,function(res:MyApiResult){
|
||
console.log("connect", res)
|
||
// type 0 成功 10000 蓝牙连接失败 10001 蓝牙异常断开 10002 表示蓝牙未开启 10003表示无蓝牙权限 10004 表示蓝牙已经连接 10005 鸿蒙虚拟蓝牙地址无效
|
||
if(res.type == 0){
|
||
lib.scanServices(function(serviceRes:MyApiResult){
|
||
console.log("services", serviceRes)
|
||
if(serviceRes.type == 0){
|
||
let uuidList = lib.getUUIDs() as WNuuid[]
|
||
console.log("uuidList", uuidList)
|
||
let uuids = lib.getUUIDs()
|
||
console.log("uuids", uuids)
|
||
if(uuids.length > 0){
|
||
lib.setSelectUUID(0)
|
||
lib.onNotityBleData(
|
||
lib.getServiceUUID(),
|
||
lib.getNotityUUID(),
|
||
true,
|
||
function(notifyRes:MyApiResult){
|
||
// type 为0 表示通知数据 1000 表示订阅成功 1001 表示订阅失败
|
||
if(notifyRes.type == 0){
|
||
let data = notifyRes.data as NotityData
|
||
console.log("notify", data)
|
||
}
|
||
}
|
||
)
|
||
let writeData:WriteData = {
|
||
serviceId:lib.getServiceUUID(),
|
||
characteristicId:lib.getwriteUUID(),
|
||
fenbao:false,
|
||
hexStrData:"FF001122"
|
||
}
|
||
lib.sendData(writeData,function(sendRes:MyApiResult){
|
||
console.log("send", sendRes)
|
||
})
|
||
let readData:WriteData = {
|
||
serviceId:lib.getServiceUUID(),
|
||
characteristicId:lib.getwriteUUID(),
|
||
fenbao:false,
|
||
data:[0xff,0x00,0x11,0x22] as number[]
|
||
}
|
||
lib.sendData(readData,function(sendRes:MyApiResult){
|
||
console.log("send", sendRes)
|
||
})
|
||
}
|
||
}
|
||
})
|
||
}
|
||
})
|
||
}
|
||
}
|
||
}
|
||
|
||
lib.onStartScanBle(scanPara)
|
||
```
|
||
|
||
### `uni-app` 快速上手
|
||
|
||
```js
|
||
import { BleLib } from "@/uni_modules/android-ble"
|
||
|
||
var lib = new BleLib()
|
||
var firstMac = ""
|
||
|
||
lib.openBtBluetooth(function(res){
|
||
console.log("openBtBluetooth", res)
|
||
})
|
||
|
||
lib.onBtOpenStateListener(function(res){
|
||
console.log("bt state", res)
|
||
})
|
||
|
||
lib.onStartScanBle({
|
||
scantime:10 * 1000,
|
||
showEmptyName:0,
|
||
btNameFilter:"",
|
||
onScanResult:function(res){
|
||
// type 为0 扫描成功 10001 表示无蓝牙权限 10002 表示蓝牙未开启
|
||
if(res.type == 0){
|
||
var scan = res.data
|
||
console.log("scan", scan)
|
||
if(firstMac == ""){
|
||
firstMac = scan.device.address
|
||
}
|
||
}
|
||
},
|
||
scanComplate:function(){
|
||
console.log("scan complete")
|
||
if(firstMac != ""){
|
||
lib.connect(firstMac,false,function(res){
|
||
console.log("connect", res)
|
||
// type 0 成功 10000 蓝牙连接失败 10001 蓝牙异常断开 10002 表示蓝牙未开启 10003表示无蓝牙权限 10004 表示蓝牙已经连接 10005 鸿蒙虚拟蓝牙地址无效
|
||
if(res.type == 0){
|
||
lib.scanServices(function(serviceRes){
|
||
console.log("services", serviceRes)
|
||
var uuids = lib.getUUIDs()
|
||
console.log("uuids", uuids)
|
||
if(uuids.length > 0){
|
||
lib.setSelectUUID(0)
|
||
lib.onNotityBleData(
|
||
lib.getServiceUUID(),
|
||
lib.getNotityUUID(),
|
||
true,
|
||
function(notifyRes){
|
||
// type 为0 表示通知数据 1000 表示订阅成功 1001 表示订阅失败
|
||
console.log("notify", notifyRes)
|
||
}
|
||
)
|
||
lib.sendData({
|
||
serviceId:lib.getServiceUUID(),
|
||
characteristicId:lib.getwriteUUID(),
|
||
fenbao:false,
|
||
hexStrData:"FF001122"
|
||
},function(sendRes){
|
||
console.log("send", sendRes)
|
||
})
|
||
}
|
||
})
|
||
}
|
||
})
|
||
}
|
||
}
|
||
})
|
||
```
|
||
|
||
### 自定义基座
|
||
|
||
1. 在 HBuilderX 中执行云打包并制作自定义基座。
|
||
2. 基座制作完成后,运行到手机或模拟器。
|
||
3. 选择运行到 Android App 基座,并选择自定义基座。
|
||
4. 如果之前安装过旧基座,请先卸载旧版本后再安装。
|
||
|
||
### iOS 蓝牙权限配置
|
||
|
||
iOS 端除了引入插件,还需要在项目 `manifest.json` 的 `app-ios.distribute.privacyDescription` 中配置蓝牙用途说明,否则首次访问蓝牙时可能不弹权限框,系统设置里也不会出现应用的蓝牙权限开关。
|
||
|
||
参考配置:
|
||
|
||
```json
|
||
{
|
||
"app-ios": {
|
||
"distribute": {
|
||
"privacyDescription": {
|
||
"NSBluetoothAlwaysUsageDescription": "应用需要使用蓝牙扫描、连接和通信外设设备",
|
||
"NSBluetoothPeripheralUsageDescription": "应用需要使用蓝牙扫描、连接和通信外设设备"
|
||
}
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
说明:
|
||
|
||
- 修改后需要重新云打包或重新生成 iOS 安装包才会生效。
|
||
- 建议卸载旧包后再安装新包。
|
||
- iOS 一般在首次扫描、连接等真正访问蓝牙时弹权限框,不一定在应用启动瞬间弹出。
|
||
|
||
## 推荐调用流程
|
||
|
||
建议按下面顺序接入:
|
||
|
||
1. 创建 `BleLib` 实例。
|
||
2. 调用 `openBtBluetooth` 初始化蓝牙适配器。
|
||
3. 可选:调用 `onBtOpenStateListener` 监听系统蓝牙开关状态。
|
||
4. 调用 `onStartScanBle` 或 `startScanBleDevice` 扫描设备。
|
||
5. 拿到设备地址后调用 `connect` 连接。
|
||
6. 连接成功后调用 `scanServices` 扫描服务。
|
||
7. 通过 `getUUIDs` 获取自动识别出的可用服务组合,必要时调用 `setSelectUUID` 切换。
|
||
8. 调用 `onNotityBleData` 开启通知。
|
||
9. 调用 `sendData` 发送数据,或调用 `onReadData` 主动读取。
|
||
10. 完成后调用 `close` 断开连接。
|
||
|
||
iOS 已连接设备恢复场景:
|
||
|
||
- 先调用 `getConnectedDevicesWithServices` 按服务 UUID 获取系统当前已连接设备。
|
||
- 再把返回结果中的 `device.address` 传给 `redictToConnect`,让当前 `BleLib` 实例接管连接。
|
||
|
||
多设备场景建议:
|
||
|
||
- 每个蓝牙连接使用一个独立的 `BleLib` 实例。
|
||
- 可参考 `pages/Multi/Multi.uvue` 的多连接演示页面。
|
||
|
||
## 主要类型
|
||
|
||
### `ScanPara`
|
||
|
||
扫描参数,常用字段如下:
|
||
|
||
- `btNameFilter`:按名称关键字过滤
|
||
- `fliterNames`:按多个名称前缀过滤
|
||
- `scantime`:扫描时长,单位毫秒
|
||
- `showEmptyName`:是否返回空名称设备,`0` 表示不返回,`1` 表示返回
|
||
- `iosFilterUUIDs`:按 service UUID 数组过滤扫描,当前支持 iOS、Android、鸿蒙、微信小程序
|
||
- `onScanResult`:扫描结果回调
|
||
- `scanComplate`:扫描结束回调
|
||
|
||
### `WNuuid`
|
||
|
||
自动识别出的可用服务组合:
|
||
|
||
- `serviceId`
|
||
- `wuuid`
|
||
- `nuuid`
|
||
|
||
### `WriteData`
|
||
|
||
写入参数:
|
||
|
||
- `serviceId`:服务 UUID
|
||
- `characteristicId`:写入特征 UUID
|
||
- `writeType`:写入类型,`0` 为 `WRITE`,`1` 为 `WRITE_NO_RESPONSE`
|
||
- `buffer`:原始二进制数据,优先级最高
|
||
- `data`:数字数组
|
||
- `hexStrData`:十六进制字符串
|
||
- `fenbao`:是否自动分包发送
|
||
|
||
写入优先级:`buffer` > `data` > `hexStrData`
|
||
|
||
### `MyApiResult`
|
||
|
||
统一回调结构:
|
||
|
||
- `type`:状态码
|
||
- `message`:状态说明
|
||
- `data`:返回数据
|
||
|
||
### `NotityData`
|
||
|
||
通知 / indicate / 读取回调数据结构:
|
||
|
||
- `data`:字节数组
|
||
- `mac`:设备地址,部分平台返回
|
||
- `serviceId`:服务 UUID
|
||
- `characteristicsId`:特征 UUID
|
||
|
||
## API 清单
|
||
|
||
### 适配器与状态
|
||
|
||
- `isEnabled`:检测蓝牙是否开启
|
||
- `openBtBluetooth`:初始化蓝牙适配器
|
||
- `openBluetooth`:打开或关闭蓝牙
|
||
- `openBluetoothSettings`:打开蓝牙设置页
|
||
- `onBtOpenStateListener`:监听系统蓝牙开关状态
|
||
|
||
### 扫描相关
|
||
|
||
- `onStartScanBle`:新版扫描接口,推荐使用
|
||
- `startScanBleDevice`:旧版扫描接口,兼容保留
|
||
- `onScanDataListener`:监听广播信号变化
|
||
- `stopScanBle`:停止扫描
|
||
|
||
### 连接相关
|
||
|
||
- `connect`:连接蓝牙
|
||
- `close`:断开蓝牙
|
||
- `isConnected`:是否已连接
|
||
- `getConnectMac`:获取当前已连接设备地址
|
||
- `setBtConnectTimeout`:设置连接超时时间
|
||
- `setBtAutoConnectTime`:设置自动连接间隔
|
||
- `cancelAutoConnectBt`:取消自动重连
|
||
|
||
### iOS 专属连接恢复
|
||
|
||
- 本小节两个方法只有 iOS 可以调用,其他平台都不支持。
|
||
|
||
- `getConnectedDevicesWithServices`:按服务 UUID 获取系统当前已连接设备
|
||
- `redictToConnect`:接管 `getConnectedDevicesWithServices` 返回的已连接设备
|
||
|
||
### 服务与特征值
|
||
|
||
- `scanServices`:扫描当前连接设备的服务与特征值
|
||
- `getUUIDs`:获取自动识别出的可用 `service + notify + write` 组合
|
||
- `setSelectUUID`:切换当前使用的 UUID 组合
|
||
- `getSericUUID`:获取当前服务 UUID,旧命名,兼容保留
|
||
- `getServiceUUID`:获取当前服务 UUID
|
||
- `getNotityUUID`:获取当前通知 UUID
|
||
- `getWriteUUID`:获取当前写入 UUID
|
||
- `getwriteUUID`:获取当前写入 UUID,旧命名,兼容保留
|
||
|
||
### 数据通信
|
||
|
||
- `onNotityBleData`:开启或关闭通知订阅
|
||
- `onIndicate`:开启 indicate 订阅并监听设备主动上报数据
|
||
- `onReadData`:主动读取蓝牙数据
|
||
- `sendData`:写入数据,支持字符串、数组、`ArrayBuffer`
|
||
- `readRssi`:读取 RSSI
|
||
- `setMtu`:设置 MTU,仅 Android 有效
|
||
- `setPhy2MMode`:设置 Android PHY 2M 模式,仅 Android 有效
|
||
|
||
### 调试与线程
|
||
|
||
- `setDebugMode`:打开调试日志
|
||
- `setThreadTypeName`:设置回调线程名
|
||
|
||
## 方法说明与调用示例
|
||
|
||
本节按方法逐个列出用途、参数、返回值与调用示例,并同时给出 `uni-app x` 与 `uni-app` 参考写法。
|
||
|
||
### `isEnabled`
|
||
|
||
- 用途:检测当前系统蓝牙是否处于开启状态。
|
||
- 参数:无。
|
||
- 返回:`boolean`。
|
||
- 常见场景:进入页面后先判断是否需要提示用户开蓝牙。
|
||
- `uni-app x` / `uni-app`
|
||
|
||
```js
|
||
var open = lib.isEnabled()
|
||
console.log(open)
|
||
```
|
||
|
||
### `openBtBluetooth`
|
||
|
||
- 用途:初始化蓝牙适配器,并触发回调告知初始化结果。
|
||
- 参数:`callback`。
|
||
- 返回:无,结果通过回调返回。
|
||
- iOS 说明:iOS 端该方法主要用于初始化和检查蓝牙状态,不会像 Android 一样主动打开系统蓝牙。
|
||
- iOS 回调说明:`type=0` 表示蓝牙已开启,`type=10002` 表示系统蓝牙未开启,`type=10003` 表示蓝牙权限被拒绝、受限或待确认。
|
||
- 常见场景:扫描和连接前先调用一次。
|
||
- `uni-app x`
|
||
|
||
```ts
|
||
lib.openBtBluetooth(function(res:MyApiResult){
|
||
// type 为0 表示初始化成功 其它值可结合 message 查看失败原因
|
||
console.log(res)
|
||
})
|
||
```
|
||
|
||
- `uni-app`
|
||
|
||
```js
|
||
lib.openBtBluetooth(function(res){
|
||
// type 为0 表示初始化成功 其它值可结合 message 查看失败原因
|
||
console.log(res)
|
||
})
|
||
```
|
||
|
||
### `openBluetooth`
|
||
|
||
- 用途:按布尔值控制打开或关闭蓝牙。
|
||
- 参数:`on:boolean`。
|
||
- 返回:无。
|
||
- 常见场景:Android 或小程序场景下快捷控制蓝牙状态。
|
||
- `uni-app x` / `uni-app`
|
||
|
||
```js
|
||
lib.openBluetooth(true)
|
||
lib.openBluetooth(false)
|
||
```
|
||
|
||
### `openBluetoothSettings`
|
||
|
||
- 用途:跳转到系统蓝牙相关设置页。
|
||
- 参数:无。
|
||
- 返回:无。
|
||
- iOS 说明:iOS 端会跳转到当前应用的系统设置页,适合在用户拒绝蓝牙权限后引导其手动开启。
|
||
- 常见场景:蓝牙关闭且需要引导用户手动开启时使用。
|
||
- `uni-app x` / `uni-app`
|
||
|
||
```js
|
||
lib.openBluetoothSettings()
|
||
```
|
||
|
||
- 推荐搭配 `openBtBluetooth` 使用
|
||
|
||
```js
|
||
lib.openBtBluetooth(function(res){
|
||
if(res.type == 10003){
|
||
// iOS 用户拒绝蓝牙权限后,可引导用户跳转系统设置手动开启
|
||
lib.openBluetoothSettings()
|
||
}
|
||
})
|
||
```
|
||
|
||
### `onBtOpenStateListener`
|
||
|
||
- 用途:监听系统蓝牙开关状态变化。
|
||
- 参数:`callback`。
|
||
- 返回:无,结果通过回调返回。
|
||
- 常见场景:页面常驻监听蓝牙状态变化。
|
||
- `uni-app x`
|
||
|
||
```ts
|
||
lib.onBtOpenStateListener(function(res:MyApiResult){
|
||
// data 为 true 表示蓝牙开启 false 表示蓝牙关闭
|
||
console.log(res)
|
||
})
|
||
```
|
||
|
||
- `uni-app`
|
||
|
||
```js
|
||
lib.onBtOpenStateListener(function(res){
|
||
// data 为 true 表示蓝牙开启 false 表示蓝牙关闭
|
||
console.log(res)
|
||
})
|
||
```
|
||
|
||
### `onStartScanBle`
|
||
|
||
- 用途:新版 BLE 扫描接口,支持过滤条件与扫描完成回调。
|
||
- 参数:`ScanPara`。
|
||
- 返回:无,扫描结果通过 `onScanResult` 返回。
|
||
- 常见场景:绝大多数新项目建议优先使用该接口。
|
||
- 补充说明:如需按服务 UUID 过滤扫描结果,可通过 `iosFilterUUIDs` 传入 UUID 数组,当前支持 iOS、Android、鸿蒙、微信小程序。
|
||
- `uni-app x`
|
||
|
||
```ts
|
||
let scanPara:ScanPara = {
|
||
scantime:10 * 1000,
|
||
showEmptyName:0,
|
||
btNameFilter:"",
|
||
onScanResult:function(res:MyApiResult){
|
||
// type 为0 扫描成功 10001 表示无蓝牙权限 10002 表示蓝牙未开启
|
||
if(res.type == 0){
|
||
let scan = res.data as BleScanResult
|
||
console.log(scan)
|
||
}
|
||
},
|
||
scanComplate:function(){
|
||
console.log("scan complete")
|
||
}
|
||
} as ScanPara
|
||
lib.onStartScanBle(scanPara)
|
||
```
|
||
|
||
- `uni-app`
|
||
|
||
```js
|
||
lib.onStartScanBle({
|
||
scantime:10 * 1000,
|
||
showEmptyName:0,
|
||
btNameFilter:"",
|
||
onScanResult:function(res){
|
||
// type 为0 扫描成功 10001 表示无蓝牙权限 10002 表示蓝牙未开启
|
||
if(res.type == 0){
|
||
var scan = res.data
|
||
console.log(scan)
|
||
}
|
||
},
|
||
scanComplate:function(){
|
||
console.log("scan complete")
|
||
}
|
||
})
|
||
```
|
||
|
||
### `startScanBleDevice`
|
||
|
||
- 用途:旧版扫描接口,保留给旧项目兼容使用。
|
||
- 参数:扫描时长、回调函数。
|
||
- 返回:无,结果通过回调返回。
|
||
- 常见场景:已有老代码迁移成本较高时继续使用。
|
||
- `uni-app x`
|
||
|
||
```ts
|
||
lib.startScanBleDevice(15000,function(res:MyApiResult){
|
||
// type 为0 扫描成功 10001 表示无蓝牙权限 10002 表示蓝牙未开启
|
||
if(res.type == 0){
|
||
let scan = res.data as BleScanResult
|
||
console.log(scan)
|
||
}
|
||
})
|
||
```
|
||
|
||
- `uni-app`
|
||
|
||
```js
|
||
lib.startScanBleDevice(15000,function(res){
|
||
// type 为0 扫描成功 10001 表示无蓝牙权限 10002 表示蓝牙未开启
|
||
if(res.type == 0){
|
||
var scan = res.data
|
||
console.log(scan)
|
||
}
|
||
})
|
||
```
|
||
|
||
### `onScanDataListener`
|
||
|
||
- 用途:实时监听扫描过程中的广播变化与 RSSI 数据。
|
||
- 参数:`callback`。
|
||
- 返回:无,数据通过回调返回。
|
||
- 常见场景:需要显示信号强度变化或原始广播数据。
|
||
- `uni-app x`
|
||
|
||
```ts
|
||
lib.onScanDataListener(function(res:MyApiResult){
|
||
let data = res.data as ScanRssiBR
|
||
console.log(data)
|
||
})
|
||
```
|
||
|
||
- `uni-app`
|
||
|
||
```js
|
||
lib.onScanDataListener(function(res){
|
||
console.log(res)
|
||
})
|
||
```
|
||
|
||
### `stopScanBle`
|
||
|
||
- 用途:停止当前 BLE 扫描。
|
||
- 参数:无。
|
||
- 返回:无。
|
||
- 常见场景:页面离开、用户手动停止扫描、连接前停止扫描。
|
||
- `uni-app x` / `uni-app`
|
||
|
||
```js
|
||
lib.stopScanBle()
|
||
```
|
||
|
||
### `connect`
|
||
|
||
- 用途:连接指定蓝牙设备。
|
||
- 参数:设备地址、是否自动连接、回调函数。
|
||
- 返回:无,结果通过回调返回。
|
||
- 回调 `type` 说明:`0` 成功,`10000` 蓝牙连接失败,`10001` 蓝牙异常断开,`10002` 表示蓝牙未开启,`10003` 表示无蓝牙权限,`10004` 表示蓝牙已经连接,`10005` 表示鸿蒙虚拟蓝牙地址无效。
|
||
- 常见场景:用户点击扫描结果后发起连接。
|
||
- iOS 说明:若你希望控制应用退到后台后,连接成功、连接断开、收到通知时系统是否弹出唤回应用提示,请在 `connect` 前先调用 `setIOSConnectNotifyAlertEnabled`。
|
||
- `uni-app x`
|
||
|
||
```ts
|
||
lib.connect(device.device.address,false,function(res:MyApiResult){
|
||
console.log(res)
|
||
// type 0 成功 10000 蓝牙连接失败 10001 蓝牙异常断开 10002 表示蓝牙未开启 10003表示无蓝牙权限 10004 表示蓝牙已经连接 10005 鸿蒙虚拟蓝牙地址无效
|
||
if(res.type == 0){
|
||
|
||
}else{
|
||
|
||
}
|
||
})
|
||
```
|
||
|
||
### `getConnectedDevicesWithServices`
|
||
|
||
- 用途:按服务 UUID 查询 iOS 系统当前已经连接的 BLE 外设。
|
||
- 参数:`string[]`,传入目标设备对应的服务 UUID 数组。
|
||
- 返回:`MyApiResult`,`data` 为 `BleScanResult[]`。
|
||
- 平台说明:只有 iOS 可以调用;Android、鸿蒙、微信小程序、Web 都不支持该方法。
|
||
- 返回说明:返回结果中的 `device.address` 为 iOS 外设标识 `identifier.uuidString`,后续需要原样传给 `redictToConnect`。
|
||
- 常见场景:App 重启后恢复已连接设备、需要接管系统层已保持连接的外设。
|
||
- `uni-app x` / `uni-app`(仅 iOS)
|
||
|
||
```js
|
||
var serviceIds = ["FFF0"]
|
||
var res = lib.getConnectedDevicesWithServices(serviceIds)
|
||
console.log(res)
|
||
|
||
if(res.type == 0){
|
||
var list = res.data || []
|
||
if(list.length > 0){
|
||
console.log(list[0].device.address)
|
||
}
|
||
}
|
||
```
|
||
|
||
### `redictToConnect`
|
||
|
||
- 用途:把 `getConnectedDevicesWithServices` 查到的已连接 iOS 外设接管到当前 `BleLib` 实例。
|
||
- 参数:设备地址、回调函数。
|
||
- 返回:无,结果通过回调返回。
|
||
- 平台说明:只有 iOS 可以调用;Android、鸿蒙、微信小程序、Web 都不支持该方法。
|
||
- 调用前提:必须先调用 `getConnectedDevicesWithServices`,并使用其返回结果中的 `device.address` 作为第一个参数。
|
||
- 回调 `type` 说明:`0` 表示接管成功,`10001` 表示设备已断开。
|
||
- 常见场景:业务层拿到系统已连接设备后,继续调用 `scanServices`、`onNotityBleData`、`sendData` 等插件能力。
|
||
- `uni-app x` / `uni-app`(仅 iOS)
|
||
|
||
```js
|
||
var res = lib.getConnectedDevicesWithServices(["FFF0"])
|
||
if(res.type == 0){
|
||
var list = res.data || []
|
||
if(list.length > 0){
|
||
var deviceId = list[0].device.address
|
||
lib.redictToConnect(deviceId,function(connectRes){
|
||
console.log(connectRes)
|
||
if(connectRes.type == 0){
|
||
lib.scanServices(function(serviceRes){
|
||
console.log(serviceRes)
|
||
})
|
||
}
|
||
})
|
||
}
|
||
}
|
||
```
|
||
|
||
### `setIOSConnectNotifyAlertEnabled`
|
||
|
||
- 用途:控制 iOS 端在应用进入后台后,连接成功、连接断开、收到通知时系统是否显示唤回应用提示。
|
||
- 参数:`enabled:boolean`。
|
||
- 返回:无。
|
||
- 默认值:`true`,即默认允许系统显示相关提示。
|
||
- 生效时机:必须在 `connect` 前调用;连接发起后再修改,不影响当前这次连接。
|
||
- 平台说明:仅 iOS 生效,Android、鸿蒙、微信小程序为空实现,用于保持多端调用一致。
|
||
- `uni-app x` / `uni-app`
|
||
|
||
```js
|
||
lib.setIOSConnectNotifyAlertEnabled(false)
|
||
lib.connect(device.device.address,false,function(res){
|
||
console.log(res)
|
||
})
|
||
```
|
||
|
||
- `uni-app`
|
||
|
||
```js
|
||
lib.connect(device.device.address,false,function(res){
|
||
console.log(res)
|
||
// type 0 成功 10000 蓝牙连接失败 10001 蓝牙异常断开 10002 表示蓝牙未开启 10003表示无蓝牙权限 10004 表示蓝牙已经连接 10005 鸿蒙虚拟蓝牙地址无效
|
||
if(res.type == 0){
|
||
|
||
}else{
|
||
|
||
}
|
||
})
|
||
```
|
||
|
||
### `close`
|
||
|
||
- 用途:断开当前蓝牙连接并释放连接状态。
|
||
- 参数:无。
|
||
- 返回:无。
|
||
- 常见场景:页面卸载、切换设备、手动断开。
|
||
- `uni-app x` / `uni-app`
|
||
|
||
```js
|
||
lib.close()
|
||
```
|
||
|
||
### `isConnected`
|
||
|
||
- 用途:判断当前对象是否已连接蓝牙设备。
|
||
- 参数:无。
|
||
- 返回:`boolean`。
|
||
- 常见场景:发送、读取、订阅前做前置判断。
|
||
- `uni-app x` / `uni-app`
|
||
|
||
```js
|
||
var isconnect = lib.isConnected()
|
||
console.log(isconnect)
|
||
```
|
||
|
||
### `getConnectMac`
|
||
|
||
- 用途:获取当前已连接设备地址。
|
||
- 参数:无。
|
||
- 返回:设备地址字符串,未连接时通常为空字符串。
|
||
- 常见场景:业务层记录当前连接目标。
|
||
- `uni-app x` / `uni-app`
|
||
|
||
```js
|
||
var deviceId = lib.getConnectMac()
|
||
console.log(deviceId)
|
||
```
|
||
|
||
### `setBtConnectTimeout`
|
||
|
||
- 用途:设置蓝牙连接超时时间。
|
||
- 参数:超时时间,单位毫秒。
|
||
- 返回:无。
|
||
- 常见场景:外设连接较慢,需要调整默认超时值。
|
||
- `uni-app x` / `uni-app`
|
||
|
||
```js
|
||
lib.setBtConnectTimeout(12000)
|
||
```
|
||
|
||
### `setBtAutoConnectTime`
|
||
|
||
- 用途:设置自动重连的时间间隔。
|
||
- 参数:时间间隔,单位毫秒。
|
||
- 返回:无。
|
||
- 常见场景:需要控制自动连接重试节奏时使用。
|
||
- `uni-app x` / `uni-app`
|
||
|
||
```js
|
||
lib.setBtAutoConnectTime(12000)
|
||
```
|
||
|
||
### `cancelAutoConnectBt`
|
||
|
||
- 用途:取消自动重连。
|
||
- 参数:无。
|
||
- 返回:无。
|
||
- 常见场景:用户主动退出当前连接流程。
|
||
- `uni-app x` / `uni-app`
|
||
|
||
```js
|
||
lib.cancelAutoConnectBt()
|
||
```
|
||
|
||
### `scanServices`
|
||
|
||
- 用途:扫描当前连接设备的服务与特征值。
|
||
- 参数:回调函数。
|
||
- 返回:无,服务列表通过回调返回。
|
||
- 常见场景:连接成功后必须执行一次,供后续通知与写入使用。
|
||
- `uni-app x`
|
||
|
||
```ts
|
||
lib.scanServices(function(resSevice:MyApiResult){
|
||
console.log(resSevice)
|
||
if(resSevice.type == 0){
|
||
let d = resSevice.data as BleServices[]
|
||
console.log(d)
|
||
}
|
||
})
|
||
```
|
||
|
||
- `uni-app`
|
||
|
||
```js
|
||
lib.scanServices(function(resSevice){
|
||
console.log(resSevice)
|
||
})
|
||
```
|
||
|
||
### `getUUIDs`
|
||
|
||
- 用途:获取插件自动识别出的可用 `service + notify + write` 组合列表。
|
||
- 参数:无。
|
||
- 返回:`WNuuid[]`。
|
||
- 常见场景:设备存在多个服务组合时供业务层选择。
|
||
- `uni-app x` / `uni-app`
|
||
|
||
```js
|
||
var uuids = lib.getUUIDs()
|
||
console.log(uuids)
|
||
```
|
||
|
||
### `setSelectUUID`
|
||
|
||
- 用途:切换当前使用的 UUID 组合。
|
||
- 参数:组合下标。
|
||
- 返回:无。
|
||
- 常见场景:自动识别到多组候选服务时手动切换。
|
||
- `uni-app x` / `uni-app`
|
||
|
||
```js
|
||
lib.setSelectUUID(0)
|
||
```
|
||
|
||
### `getSericUUID`
|
||
|
||
- 用途:获取当前选中的服务 UUID,旧命名接口。
|
||
- 参数:无。
|
||
- 返回:`string`。
|
||
- 常见场景:兼容旧项目代码。
|
||
- `uni-app x` / `uni-app`
|
||
|
||
```js
|
||
console.log(lib.getSericUUID())
|
||
```
|
||
|
||
### `getServiceUUID`
|
||
|
||
- 用途:获取当前选中的服务 UUID。
|
||
- 参数:无。
|
||
- 返回:`string`。
|
||
- 常见场景:写入、订阅、主动读取前获取服务 UUID。
|
||
- `uni-app x` / `uni-app`
|
||
|
||
```js
|
||
console.log(lib.getServiceUUID())
|
||
```
|
||
|
||
### `getNotityUUID`
|
||
|
||
- 用途:获取当前选中的通知特征 UUID。
|
||
- 参数:无。
|
||
- 返回:`string`。
|
||
- 常见场景:开启通知前读取当前通知 UUID。
|
||
- `uni-app x` / `uni-app`
|
||
|
||
```js
|
||
console.log(lib.getNotityUUID())
|
||
```
|
||
|
||
### `getWriteUUID`
|
||
|
||
- 用途:获取当前选中的写入特征 UUID。
|
||
- 参数:无。
|
||
- 返回:`string`。
|
||
- 常见场景:发送数据前获取写入 UUID。
|
||
- `uni-app x` / `uni-app`
|
||
|
||
```js
|
||
console.log(lib.getWriteUUID())
|
||
```
|
||
|
||
### `getwriteUUID`
|
||
|
||
- 用途:获取当前选中的写入特征 UUID,旧命名接口。
|
||
- 参数:无。
|
||
- 返回:`string`。
|
||
- 常见场景:兼容旧项目代码。
|
||
- `uni-app x` / `uni-app`
|
||
|
||
```js
|
||
console.log(lib.getwriteUUID())
|
||
```
|
||
|
||
### `onNotityBleData`
|
||
|
||
- 用途:开启或关闭通知订阅,并监听设备主动上报数据。
|
||
- 参数:服务 UUID、通知 UUID、开关状态、回调函数。
|
||
- 返回:无,订阅状态与通知数据通过回调返回。
|
||
- 常见场景:连接成功并完成服务扫描后开启通知。
|
||
- `uni-app x`
|
||
|
||
```ts
|
||
lib.onNotityBleData(
|
||
lib.getSericUUID(),
|
||
lib.getNotityUUID(),
|
||
true,
|
||
function(res:MyApiResult){
|
||
// type 为0 表示通知数据 1000 表示订阅成功 1001 表示订阅失败
|
||
if(res.type == 0){
|
||
let data = res.data as NotityData
|
||
console.log(data)
|
||
}
|
||
}
|
||
)
|
||
```
|
||
|
||
### `onIndicate`
|
||
|
||
- 用途:开启 indicate 订阅,并监听设备主动上报的 indicate 数据。
|
||
- 参数:服务 UUID、indicate 特征 UUID、回调函数。
|
||
- 返回:无,订阅状态与 indicate 数据通过回调返回。
|
||
- 常见场景:设备特征值属性为 `INDICATE`,需要带确认机制的数据上报时使用。
|
||
- 平台说明:Android、iOS、鸿蒙已支持真实 indicate 订阅;微信小程序端当前未实现该接口。
|
||
- `uni-app x`
|
||
|
||
```ts
|
||
lib.onIndicate(
|
||
"serviceUUID",
|
||
"indicateUUID",
|
||
function(res:MyApiResult){
|
||
// type 为0 表示 indicate 数据 1000 表示订阅成功 1001 表示订阅失败
|
||
if(res.type == 0){
|
||
let data = res.data as NotityData
|
||
console.log(data)
|
||
}
|
||
}
|
||
)
|
||
```
|
||
|
||
- `uni-app`
|
||
|
||
```js
|
||
lib.onIndicate(
|
||
"serviceUUID",
|
||
"indicateUUID",
|
||
function(res){
|
||
// type 为0 表示 indicate 数据 1000 表示订阅成功 1001 表示订阅失败
|
||
console.log(res)
|
||
}
|
||
)
|
||
```
|
||
|
||
- `uni-app`
|
||
|
||
```js
|
||
lib.onNotityBleData(
|
||
lib.getSericUUID(),
|
||
lib.getNotityUUID(),
|
||
true,
|
||
function(res){
|
||
// type 为0 表示通知数据 1000 表示订阅成功 1001 表示订阅失败
|
||
console.log(res)
|
||
}
|
||
)
|
||
```
|
||
|
||
### `onReadData`
|
||
|
||
- 用途:主动读取某个特征值的数据。
|
||
- 参数:服务 UUID、读取 UUID、回调函数。
|
||
- 返回:无,读取结果通过回调返回。
|
||
- 常见场景:设备不主动通知,需要业务端手动拉取数据。
|
||
- `uni-app x`
|
||
|
||
```ts
|
||
lib.onReadData("sericeUUID","readUUID",function(res:MyApiResult){
|
||
console.log(res)
|
||
})
|
||
```
|
||
|
||
- `uni-app`
|
||
|
||
```js
|
||
lib.onReadData("sericeUUID","readUUID",function(res){
|
||
console.log(res)
|
||
})
|
||
```
|
||
|
||
### `sendData`
|
||
|
||
- 用途:向指定特征值写入数据。
|
||
- 参数:`WriteData`、回调函数。
|
||
- 返回:无,写入结果通过回调返回。
|
||
- 常见场景:发送控制指令、配置参数、透传数据。
|
||
- `uni-app x` 发送 16 进制字符串
|
||
|
||
```ts
|
||
let writeData:WriteData = {
|
||
serviceId:lib.getServiceUUID(),
|
||
characteristicId:lib.getwriteUUID(),
|
||
fenbao:false,
|
||
hexStrData:"FF001122"
|
||
}
|
||
lib.sendData(writeData,function(res:MyApiResult){
|
||
// 发送结果可结合 type 和 message 判断是否成功
|
||
console.log(res)
|
||
})
|
||
```
|
||
|
||
- `uni-app` 发送 16 进制字符串
|
||
|
||
```js
|
||
lib.sendData({
|
||
serviceId:lib.getServiceUUID(),
|
||
characteristicId:lib.getwriteUUID(),
|
||
fenbao:false,
|
||
hexStrData:"FF001122"
|
||
},function(res){
|
||
console.log(res)
|
||
})
|
||
```
|
||
|
||
- `uni-app x` 发送字节数组
|
||
|
||
```ts
|
||
let writeBytes:WriteData = {
|
||
serviceId:lib.getServiceUUID(),
|
||
characteristicId:lib.getwriteUUID(),
|
||
fenbao:false,
|
||
data:[0xff,0x00,0x11,0x22] as number[]
|
||
}
|
||
lib.sendData(writeBytes,function(res:MyApiResult){
|
||
// 发送结果可结合 type 和 message 判断是否成功
|
||
console.log(res)
|
||
})
|
||
```
|
||
|
||
- `uni-app` 发送字节数组
|
||
|
||
```js
|
||
lib.sendData({
|
||
serviceId:lib.getServiceUUID(),
|
||
characteristicId:lib.getwriteUUID(),
|
||
fenbao:false,
|
||
data:[0xff,0x00,0x11,0x22]
|
||
},function(res){
|
||
console.log(res)
|
||
})
|
||
```
|
||
|
||
- `uni-app x` 发送 `ArrayBuffer`
|
||
|
||
```ts
|
||
let buffer = new ArrayBuffer(4)
|
||
let view = new Uint8Array(buffer)
|
||
view[0] = 0xff
|
||
view[1] = 0x00
|
||
view[2] = 0x11
|
||
view[3] = 0x22
|
||
let writeBuffer:WriteData = {
|
||
serviceId:lib.getServiceUUID(),
|
||
characteristicId:lib.getwriteUUID(),
|
||
fenbao:false,
|
||
buffer:buffer
|
||
}
|
||
lib.sendData(writeBuffer,function(res:MyApiResult){
|
||
console.log(res)
|
||
})
|
||
```
|
||
|
||
### `readRssi`
|
||
|
||
- 用途:读取当前连接设备的 RSSI 信号强度。
|
||
- 参数:回调函数。
|
||
- 返回:无,读取结果通过回调返回。
|
||
- 常见场景:显示连接质量或调试信号强度。
|
||
- `uni-app x`
|
||
|
||
```ts
|
||
lib.readRssi(function(res:MyApiResult){
|
||
console.log(res)
|
||
})
|
||
```
|
||
|
||
- `uni-app`
|
||
|
||
```js
|
||
lib.readRssi(function(res){
|
||
console.log(res)
|
||
})
|
||
```
|
||
|
||
### `setMtu`
|
||
|
||
- 用途:设置 BLE 连接的 MTU。
|
||
- 参数:MTU 值、回调函数。
|
||
- 返回:无,设置结果通过回调返回。
|
||
- 常见场景:需要发送较大数据包时在 Android 端提高 MTU。
|
||
- `uni-app x`
|
||
|
||
```ts
|
||
lib.setMtu(512,function(res:MyApiResult){
|
||
console.log(res)
|
||
})
|
||
```
|
||
|
||
- `uni-app`
|
||
|
||
```js
|
||
lib.setMtu(512,function(res){
|
||
console.log(res)
|
||
})
|
||
```
|
||
|
||
### `setPhy2MMode`
|
||
|
||
- 用途:切换 Android BLE PHY 2M 模式。
|
||
- 参数:无。
|
||
- 返回:无。
|
||
- 常见场景:Android 设备支持高物理层速率时启用。
|
||
- `uni-app x` / `uni-app`
|
||
|
||
```js
|
||
lib.setPhy2MMode()
|
||
```
|
||
|
||
### `setDebugMode`
|
||
|
||
- 用途:打开或关闭插件内部调试日志。
|
||
- 参数:`debug:boolean`。
|
||
- 返回:无。
|
||
- 常见场景:联调阶段查看内部扫描、连接、通知过程。
|
||
- `uni-app x` / `uni-app`
|
||
|
||
```js
|
||
lib.setDebugMode(true)
|
||
lib.setDebugMode(false)
|
||
```
|
||
|
||
### `setThreadTypeName`
|
||
|
||
- 用途:设置插件回调线程名。
|
||
- 参数:线程名字符串。
|
||
- 返回:无。
|
||
- 常见场景:需要和现有线程调度策略保持一致时使用。
|
||
- `uni-app x` / `uni-app`
|
||
|
||
```js
|
||
lib.setThreadTypeName("main")
|
||
```
|
||
|
||
## 常用返回码
|
||
|
||
不同接口返回码略有差异,常见约定如下:
|
||
|
||
| type | 说明 |
|
||
| --- | --- |
|
||
| `0` | 成功 |
|
||
| `1000` | 通知或 indicate 订阅成功 |
|
||
| `1001` | 通知或 indicate 订阅失败,或部分平台表示蓝牙异常断开 |
|
||
| `10000` | 连接失败、读取失败或通用失败 |
|
||
| `10001` | 蓝牙异常断开,或部分场景表示无权限 |
|
||
| `10002` | 蓝牙未开启 |
|
||
| `10003` | 无蓝牙权限 |
|
||
| `10004` | 蓝牙已经连接 |
|
||
| `10005` | 部分平台用于地址无效或未连接 |
|
||
|
||
说明:不同平台和不同接口对错误码的语义可能略有差异,建议结合 `message` 一起判断。
|
||
|
||
## 平台注意事项
|
||
|
||
### Android
|
||
|
||
- 运行原生 BLE 能力建议使用自定义基座。
|
||
- `setMtu` 与 `setPhy2MMode` 为 Android 特有能力。
|
||
- Android 12 及以上系统请注意蓝牙权限授权。
|
||
- Android 端支持通过 `iosFilterUUIDs` 按服务 UUID 过滤扫描。
|
||
- Android 端现已支持真实 `onIndicate` 订阅与数据回调。
|
||
|
||
### iOS
|
||
|
||
- 仅支持 `iOS 13` 及以上。
|
||
- iOS 扫描在部分系统版本下建议配合 `iosFilterUUIDs` 使用。
|
||
- 多连接与多通知场景已做兼容,但仍建议每个连接使用独立实例。
|
||
- 若应用进入后台后不希望系统在连接、断开、通知事件发生时弹出唤回应用提示,可在连接前调用 `setIOSConnectNotifyAlertEnabled(false)`。
|
||
- 该开关底层对应 iOS 连接参数中的 `CBConnectPeripheralOptionNotifyOnConnectionKey`、`CBConnectPeripheralOptionNotifyOnDisconnectionKey`、`CBConnectPeripheralOptionNotifyOnNotificationKey`。
|
||
- iOS 端现已支持真实 `onIndicate` 订阅与数据回调。
|
||
- `getConnectedDevicesWithServices` 与 `redictToConnect` 只有 iOS 可以调用,Android、鸿蒙、微信小程序、Web 都不支持;推荐按“先查已连接设备,再传 `device.address` 接管连接”的顺序调用。
|
||
|
||
### 鸿蒙
|
||
|
||
- 部分虚拟蓝牙地址场景可能返回特定错误码。
|
||
- 建议在真机环境验证扫描、连接、RSSI 与发送流程。
|
||
- 鸿蒙端支持通过 `iosFilterUUIDs` 按服务 UUID 过滤扫描。
|
||
- 鸿蒙端现已支持真实 `onIndicate` 订阅与数据回调。
|
||
|
||
### 微信小程序
|
||
|
||
- 需在微信小程序蓝牙权限与机型能力允许的前提下运行。
|
||
- 建议一个 `BleLib` 实例绑定一个设备连接。
|
||
- 支持多对象多蓝牙连接。
|
||
- 微信小程序端支持通过 `iosFilterUUIDs` 按服务 UUID 过滤扫描。
|
||
|
||
## 示例入口
|
||
|
||
- `App.uvue`:基础单连接示例
|
||
- `pages/Multi/Multi.uvue`:多连接、UUID 选择、通知与发送示例
|
||
|
||
如果你需要查看完整调用方式,优先参考项目内这两个页面的实际示例。
|
||
|
||
## 旧接口说明
|
||
|
||
历史接口与旧版示例已保留在 `old.md` 中,主要用于兼容旧项目:
|
||
|
||
- `onNotityReadBleData`
|
||
- `writeDataToBle`
|
||
- `writeDataToBleWithType`
|
||
- `writeStringDataToBle`
|
||
|
||
新项目建议优先使用:
|
||
|
||
- `onNotityBleData`
|
||
- `sendData`
|
||
- `getServiceUUID`
|
||
- `getWriteUUID`
|
||
|
||
## 更新记录
|
||
|
||
版本变更请查看 `changelog.md`。
|