1# 如何进行蓝牙连接 2 3## 场景说明 4蓝牙技术是一种无线数据和语音通信开放的全球规范,它是基于低成本的近距离无线连接,为固定和移动设备建立通信环境的一种特殊的连接。本示例通过[@ohos.bluetoothManager](../application-dev/reference/apis-connectivity-kit/js-apis-bluetoothManager.md)接口实现蓝牙设备发现、配对、取消配对功能。 5 6## 效果呈现 7 8本示例最终效果如下: 9 10| 发现设备 | 连接设备 | 断开连接 | 11| ------------------------------- | --------------------------------- | --------------------------------- | 12|  |  |  | 13 14## 运行环境 15 16本例基于以下环境开发,开发者也可以基于其他适配的版本进行开发。 17 18- IDE:DevEco Studio 3.1.1 Release 19- SDK:Ohos_sdk_full 4.0.8.5(API Version 10 Beta1) 20 21## 实现思路 22本文涉及到蓝牙的设备发现、配对、取消配对三个功能特性,实现思路如下: 23 24- 启动和关闭蓝牙:在Index页面中通过Toggle组件的onChange函数控制蓝牙的开关,开关打开的情况下执行initBluetooth函数,关闭的情况下执行bluetooth.disableBluetooth()方法来断开蓝牙; 25- 验证蓝牙是否处于连接状态:蓝牙打开的时候通过bluetooth.on('stateChange')方法监听蓝牙连接状态改变事件,如确认已打开,执行foundDevices()函数来查找设备接口,确认已关闭则执行bluetooth.stopBluetoothDiscovery()方法停止查询接口。 26- 发现设备:在foundDevices()函数中通过bluetooth.on('bluetoothDeviceFind')方法监听设备发现事件,通过bluetooth.getPairedDevices()方法更新已配对蓝牙地址,然后通过bluetooth.startBluetoothDiscovery()方法开启蓝牙扫描发现远端设备,并且通过bluetooth.setBluetoothScanMode()方法来被远端设备发现。 27- 蓝牙配对:通过bluetooth.on('pinRequired')方法监听远端蓝牙设备的配对请求事件,点击配对执行bluetooth.setDevicePairingConfirmation(this.data.deviceId, true)方法,点击取消执行bluetooth.setDevicePairingConfirmation(this.data.deviceId, false)方法。 28- 取消配对:使用bluetooth.cancelPairedDevice()断开指定的远端设备连接。 29 30## 开发步骤 311. 申请蓝牙权限。 32 使用蓝牙需要先申请对应的权限,在module.json5文件中添加以下配置: 33 34 ```json 35 "requestPermissions": [ 36 { 37 //允许应用查看蓝牙的配置 38 "name": "ohos.permission.USE_BLUETOOTH", 39 "reason": "$string:grant_use_bluetooth", 40 "usedScene": { 41 "abilities": [ 42 "MainAbility" 43 ], 44 "when": "inuse" 45 } 46 }, 47 { 48 //允许应用配置本地蓝牙,查找远端设备且与之配对连接 49 "name": "ohos.permission.DISCOVER_BLUETOOTH", 50 "reason": "$string:grant_discovery_bluetooth", 51 "usedScene": { 52 "abilities": [ 53 "MainAbility" 54 ], 55 "when": "inuse" 56 } 57 }, 58 { 59 //允许应用获取设备位置信息 60 "name": "ohos.permission.LOCATION", 61 "reason": "$string:grant_location", 62 "usedScene": { 63 "abilities": [ 64 "MainAbility" 65 ], 66 "when": "inuse" 67 } 68 }, 69 { 70 //允许应用获取设备模糊位置信息 71 "name": "ohos.permission.APPROXIMATELY_LOCATION", 72 "reason": "$string:grant_location", 73 "usedScene": { 74 "abilities": [ 75 "MainAbility" 76 ], 77 "when": "inuse" 78 } 79 }, 80 { 81 //允许应用配对蓝牙设备,并对设备的电话簿或消息进行访问 82 "name": "ohos.permission.MANAGE_BLUETOOTH", 83 "reason": "$string:grant_manage_bluetooth", 84 "usedScene": { 85 "abilities": [ 86 "MainAbility" 87 ], 88 "when": "inuse" 89 } 90 } 91 ] 92 ``` 93 942. 构建UI框架,整体的UI框架分为TitleBar(页面名称),PinDialog(配对蓝牙弹框),index(主页面)三个部分。 95 96 ```ts 97 //Common/TitleBar.ets 98 @Component 99 export struct TitleBar { 100 private handlerClickButton: () => void 101 102 build() { 103 Row() { 104 Image($r('app.media.ic_back')) 105 .width(40) 106 .height(30) 107 .onClick(() => { 108 this.handlerClickButton() 109 }) 110 Text($r('app.string.bluetooth')) 111 .fontSize(30) 112 .width(150) 113 .height(50) 114 .margin({ left: 15 }) 115 .fontColor('#ffa2a3a4') 116 } 117 .width('100%') 118 .height(60) 119 .padding({ left: 20, top: 10 }) 120 .backgroundColor('#ff2d30cb') 121 .constraintSize({ minHeight: 50 }) 122 } 123 } 124 125 //Common/PinDalog.ets 126 ... 127 aboutToAppear() { 128 this.titleText = `"${this.data.deviceId}"要与您配对。请确认此配对码已在"${this.data.deviceId}"上直接显示,且不是手动输入的。` 129 this.pinCode = JSON.stringify(this.data.pinCode) 130 } 131 build() { 132 //配对弹框描述文字 133 Column({ space: 10 }) { 134 Text($r('app.string.match_request')) 135 .fontSize(30) 136 .alignSelf(ItemAlign.Start) 137 Text(this.titleText) 138 .alignSelf(ItemAlign.Start) 139 .margin({ top: 20 }) 140 .fontSize(21) 141 Text(this.pinCode) 142 .fontSize(40) 143 .fontWeight(FontWeight.Bold) 144 .margin({ top: 20 }) 145 Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center }) { 146 Checkbox({ name: 'checkbox' }) 147 .select(false) 148 .selectedColor('#ff3d6fb8') 149 .key('checkBox') 150 Text($r('app.string.grant_permission')) 151 .fontSize(15) 152 .margin({ left: 3, top: 6 }) 153 } 154 .alignSelf(ItemAlign.Start) 155 .width('95%') 156 .margin({ top: 5 }) 157 158 Row() { 159 //配对选择UI,取消or配对 160 this.choiceText($r('app.string.cancel'), () => { 161 bluetooth.setDevicePairingConfirmation(this.data.deviceId, false) 162 logger.info(TAG, `setDevicePairingConfirmation = ${bluetooth.setDevicePairingConfirmation(this.data.deviceId, false)}`) 163 this.controller.close() 164 }) 165 166 Divider() 167 .vertical(true) 168 .height(32) 169 170 this.choiceText($r('app.string.match'), () => { 171 bluetooth.setDevicePairingConfirmation(this.data.deviceId, true) 172 logger.info(TAG, `setDevicePairingConfirmation = ${bluetooth.setDevicePairingConfirmation(this.data.deviceId, true)}`) 173 this.controller.close() 174 }) 175 } 176 .margin({ top: 20 }) 177 } 178 .width('100%') 179 .padding(15) 180 } 181 ... 182 183 //pages/index.ets 184 @Entry 185 @Component 186 struct Index { 187 build() { 188 Column() { 189 TitleBar({ handlerClickButton: this.handlerClickButton }) 190 Scroll() { 191 Column() { 192 Row() { 193 //蓝牙开关 194 Column() { 195 Text($r('app.string.bluetooth')) 196 .fontSize(30) 197 .margin({ top: 20 }) 198 .alignSelf(ItemAlign.Start) 199 if (true === this.isOn) { 200 Text($r('app.string.discovery')) 201 .fontSize(20) 202 .alignSelf(ItemAlign.Start) 203 } 204 } 205 206 Blank() 207 208 Column() { 209 Toggle({ type: ToggleType.Switch, isOn: this.isOn }) 210 .selectedColor('#ff2982ea') 211 .onChange((isOn: boolean) => { 212 if (isOn) { 213 this.isOn = true 214 this.initBluetooth() 215 } else { 216 this.isOn = false 217 bluetooth.disableBluetooth() 218 this.deviceList = [] 219 this.discoveryList = [] 220 } 221 }) 222 } 223 .id('toggleBtn') 224 } 225 .width('90%') 226 227 if (this.isOn) { 228 Divider() 229 .vertical(false) 230 .strokeWidth(10) 231 .color('#ffece7e7') 232 .lineCap(LineCapStyle.Butt) 233 .margin('1%') 234 //已配对的设备 235 Text($r('app.string.paired_device')) 236 .fontSize(25) 237 .fontColor('#ff565555') 238 .margin({ left: '5%' }) 239 .alignSelf(ItemAlign.Start) 240 241 ForEach(this.deviceList, (item, index) => { 242 Row() { 243 Text(item) 244 .fontSize(20) 245 } 246 .alignSelf(ItemAlign.Start) 247 .width('100%') 248 .height(50) 249 .margin({ left: '5%', top: '1%' }) 250 .id(`pairedDevice${index}`) 251 .onClick(() => { 252 AlertDialog.show({ 253 //取消配对 254 title: $r('app.string.disconnect'), 255 message: '此操作将会断开您与以下设备的连接:' + item, 256 primaryButton: { 257 value: $r('app.string.cancel'), 258 action: () => { 259 } 260 }, 261 //确认取消 262 secondaryButton: { 263 value: $r('app.string.confirm'), 264 action: () => { 265 try { 266 bluetooth.cancelPairedDevice(item); 267 this.deviceList = bluetooth.getPairedDevices() 268 this.discoveryList = [] 269 bluetooth.startBluetoothDiscovery() 270 } catch (err) { 271 console.error("errCode:" + err.code + ",errMessage:" + err.message); 272 } 273 } 274 } 275 }) 276 }) 277 }) 278 279 Divider() 280 .vertical(false) 281 .strokeWidth(10) 282 .color('#ffece7e7') 283 .lineCap(LineCapStyle.Butt) 284 .margin('1%') 285 286 Text($r('app.string.available_device')) 287 .fontSize(25) 288 .fontColor('#ff565555') 289 .margin({ left: '5%', bottom: '2%' }) 290 .alignSelf(ItemAlign.Start) 291 //可用设备列表 292 ForEach(this.discoveryList, (item) => { 293 Row() { 294 Text(item) 295 .fontSize(20) 296 } 297 .alignSelf(ItemAlign.Start) 298 .width('100%') 299 .height(50) 300 .margin({ left: '5%', top: '1%' }) 301 //进行配对操作点击 302 .onClick(() => { 303 logger.info(TAG, `start bluetooth.pairDevice,item = ${item}`) 304 let pairStatus = bluetooth.pairDevice(item) 305 logger.info(TAG, `pairStatus = ${pairStatus}`) 306 }) 307 308 Divider() 309 .vertical(false) 310 .color('#ffece7e7') 311 .lineCap(LineCapStyle.Butt) 312 .margin('1%') 313 }) 314 } 315 } 316 } 317 .constraintSize({ maxHeight: '85%' }) 318 } 319 } 320 } 321 ``` 322 3233. 蓝牙开关打开关闭操作,需要打开蓝牙开关才能进行后续操作: 324 ```ts 325 initBluetooth() { 326 bluetooth.on('stateChange', (data) => { 327 logger.info(TAG, `enter on stateChange`) 328 //判断蓝牙开关状态 329 if (data === bluetooth.BluetoothState.STATE_ON) { 330 logger.info(TAG, `enter BluetoothState.STATE_ON`) 331 //蓝牙打开后的相关操作 332 this.foundDevices() 333 } 334 if (data === bluetooth.BluetoothState.STATE_OFF) { 335 logger.info(TAG, `enter BluetoothState.STATE_OFF`) 336 bluetooth.off('bluetoothDeviceFind', (data) => { 337 logger.info(TAG, `offBluetoothDeviceFindData = ${JSON.stringify(data)}`) 338 }) 339 //关闭 340 bluetooth.stopBluetoothDiscovery() 341 this.discoveryList = [] 342 } 343 logger.info(TAG, `BluetoothState = ${JSON.stringify(data)}`) 344 }) 345 //开启蓝牙 346 bluetooth.enableBluetooth() 347 } 348 ``` 349 3504. 设置蓝牙扫描模式并开启扫描去发现设备,并订阅蓝牙设备发现上报时间获取设备列表 351 ```ts 352 foundDevices() { 353 //订阅蓝牙设备发现上报事件 354 bluetooth.on('bluetoothDeviceFind', (data) => { 355 logger.info(TAG, `enter on bluetoothDeviceFind`) 356 if (data !== null && data.length > 0) { 357 if (this.discoveryList.indexOf(data[0]) === -1 && this.deviceList.indexOf(data[0]) === -1) { 358 this.discoveryList.push(data[0]) 359 } 360 logger.info(TAG, `discoveryList = ${JSON.stringify(this.discoveryList)}`) 361 } 362 let list = bluetooth.getPairedDevices() 363 if (list !== null && list.length > 0) { 364 this.deviceList = list 365 logger.info(TAG, `deviceList = ${JSON.stringify(this.deviceList)}`) 366 } 367 }) 368 //开启蓝牙扫描,可以发现远端设备 369 bluetooth.startBluetoothDiscovery() 370 //设置蓝牙扫描模式,可以被远端设备发现 371 bluetooth.setBluetoothScanMode(bluetooth.ScanMode.SCAN_MODE_CONNECTABLE_GENERAL_DISCOVERABLE, TIME) 372 } 373 ``` 374 3755. 设置蓝牙扫描模式并开启扫描去发现设备,并订阅蓝牙设备发现上报时间获取设备列表 376 377 ```ts 378 //配对确定和取消代码在PinDialog.ets文件中 379 //setDevicePairingConfirmation(device: string, accept: boolean): void 380 //device string 表示远端设备地址,例如:"XX:XX:XX:XX:XX:XX 381 //accept boolean 接受配对请求设置为true,否则设置为false 382 383 //订阅蓝牙配对状态改变事件,根据蓝牙状态更新设备列表 384 bluetooth.on('bondStateChange', (data) => { 385 logger.info(TAG, `enter bondStateChange`) 386 logger.info(TAG, `data = ${JSON.stringify(data)}`) 387 if (data.state === bluetooth.BondState.BOND_STATE_BONDED) { 388 logger.info(TAG, `BOND_STATE_BONDED`) 389 let index = this.discoveryList.indexOf(data.deviceId) 390 this.discoveryList.splice(index, 1) 391 this.deviceList = bluetooth.getPairedDevices() 392 } 393 if (data.state === bluetooth.BondState.BOND_STATE_INVALID) { 394 logger.info(TAG, `BOND_STATE_INVALID`) 395 this.deviceList = bluetooth.getPairedDevices() 396 } 397 logger.info(TAG, `bondStateChange,data = ${JSON.stringify(data)}`) 398 }) 399 ``` 400 401 402 403## 完整代码 404 405本例完整代码sample示例链接:[蓝牙Sample](https://gitee.com/openharmony/applications_app_samples/tree/master/code/SystemFeature/Connectivity/Bluetooth) 406 407 408 409## 参考 410- [权限申请指导](../application-dev/security/AccessToken/declare-permissions.md) 411- [@ohos.bluetooth](../application-dev/reference/apis-connectivity-kit/js-apis-bluetooth.md) 412 413