鸿蒙Harmony开发案例教程:如何进行蓝牙设备发现、配对、取消配对功能

news2024/11/23 8:59:09

如何进行蓝牙连接

场景说明

蓝牙技术是一种无线数据和语音通信开放的全球规范,它是基于低成本的近距离无线连接,为固定和移动设备建立通信环境的一种特殊的连接。本示例通过@ohos.bluetoothManager接口实现蓝牙设备发现、配对、取消配对功能。

效果呈现

本示例最终效果如下:

发现设备连接设备断开连接

 点击领取→纯血鸿蒙Next全套最新学习资料

运行环境

本例基于以下环境开发,开发者也可以基于其他适配的版本进行开发。

  • IDE:DevEco Studio 3.1.1 Release
  • SDK:Ohos_sdk_full 4.0.8.5(API Version 10 Beta1)

实现思路

本文涉及到蓝牙的设备发现、配对、取消配对三个功能特性,实现思路如下:

  • 启动和关闭蓝牙:在Index页面中通过Toggle组件的onChange函数控制蓝牙的开关,开关打开的情况下执行initBluetooth函数,关闭的情况下执行bluetooth.disableBluetooth()方法来断开蓝牙;
  • 验证蓝牙是否处于连接状态:蓝牙打开的时候通过bluetooth.on('stateChange')方法监听蓝牙连接状态改变事件,如确认已打开,执行foundDevices()函数来查找设备接口,确认已关闭则执行bluetooth.stopBluetoothDiscovery()方法停止查询接口。
  • 发现设备:在foundDevices()函数中通过bluetooth.on('bluetoothDeviceFind')方法监听设备发现事件,通过bluetooth.getPairedDevices()方法更新已配对蓝牙地址,然后通过bluetooth.startBluetoothDiscovery()方法开启蓝牙扫描发现远端设备,并且通过bluetooth.setBluetoothScanMode()方法来被远端设备发现。
  • 蓝牙配对:通过bluetooth.on('pinRequired')方法监听远端蓝牙设备的配对请求事件,点击配对执行bluetooth.setDevicePairingConfirmation(this.data.deviceId, true)方法,点击取消执行bluetooth.setDevicePairingConfirmation(this.data.deviceId, false)方法。
  • 取消配对:使用bluetooth.cancelPairedDevice()断开指定的远端设备连接。

开发步骤

  1. 申请蓝牙权限。 使用蓝牙需要先申请对应的权限,在module.json5文件中添加以下配置:

    "requestPermissions": [
          {
            //允许应用查看蓝牙的配置
            "name": "ohos.permission.USE_BLUETOOTH",
            "reason": "$string:grant_use_bluetooth",
            "usedScene": {
              "abilities": [
                "MainAbility"
              ],
              "when": "inuse"
            }
          },
          {
            //允许应用配置本地蓝牙,查找远端设备且与之配对连接
            "name": "ohos.permission.DISCOVER_BLUETOOTH",
            "reason": "$string:grant_discovery_bluetooth",
            "usedScene": {
              "abilities": [
                "MainAbility"
              ],
              "when": "inuse"
            }
          },
          {
            //允许应用获取设备位置信息
            "name": "ohos.permission.LOCATION",
            "reason": "$string:grant_location",
            "usedScene": {
              "abilities": [
                "MainAbility"
              ],
              "when": "inuse"
            }
          },
          {
            //允许应用获取设备模糊位置信息
            "name": "ohos.permission.APPROXIMATELY_LOCATION",
            "reason": "$string:grant_location",
            "usedScene": {
              "abilities": [
                "MainAbility"
              ],
              "when": "inuse"
            }
          },
          {
            //允许应用配对蓝牙设备,并对设备的电话簿或消息进行访问
            "name": "ohos.permission.MANAGE_BLUETOOTH",
            "reason": "$string:grant_manage_bluetooth",
            "usedScene": {
              "abilities": [
                "MainAbility"
              ],
              "when": "inuse"
            }
          }
        ]
  2. 构建UI框架,整体的UI框架分为TitleBar(页面名称),PinDialog(配对蓝牙弹框),index(主页面)三个部分。

    //Common/TitleBar.ets
    @Component
    export struct TitleBar {
      private handlerClickButton: () => void
    
      build() {
        Row() {
          Image($r('app.media.ic_back'))
            .width(40)
            .height(30)
            .onClick(() => {
              this.handlerClickButton()
            })
          Text($r('app.string.bluetooth'))
            .fontSize(30)
            .width(150)
            .height(50)
            .margin({ left: 15 })
            .fontColor('#ffa2a3a4')
        }
        .width('100%')
        .height(60)
        .padding({ left: 20, top: 10 })
        .backgroundColor('#ff2d30cb')
        .constraintSize({ minHeight: 50 })
      }
    }
    
    //Common/PinDalog.ets
     ...
     aboutToAppear() {
        this.titleText = `"${this.data.deviceId}"要与您配对。请确认此配对码已在"${this.data.deviceId}"上直接显示,且不是手动输入的。`
        this.pinCode = JSON.stringify(this.data.pinCode)
      }
      build() {
        //配对弹框描述文字
        Column({ space: 10 }) {
          Text($r('app.string.match_request'))
            .fontSize(30)
            .alignSelf(ItemAlign.Start)
          Text(this.titleText)
            .alignSelf(ItemAlign.Start)
            .margin({ top: 20 })
            .fontSize(21)
          Text(this.pinCode)
            .fontSize(40)
            .fontWeight(FontWeight.Bold)
            .margin({ top: 20 })
          Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center }) {
            Checkbox({ name: 'checkbox' })
              .select(false)
              .selectedColor('#ff3d6fb8')
              .key('checkBox')
            Text($r('app.string.grant_permission'))
              .fontSize(15)
              .margin({ left: 3, top: 6 })
          }
          .alignSelf(ItemAlign.Start)
          .width('95%')
          .margin({ top: 5 })
    
          Row() {
            //配对选择UI,取消or配对
            this.choiceText($r('app.string.cancel'), () => {
              bluetooth.setDevicePairingConfirmation(this.data.deviceId, false)
              logger.info(TAG, `setDevicePairingConfirmation = ${bluetooth.setDevicePairingConfirmation(this.data.deviceId, false)}`)
              this.controller.close()
            })
    
            Divider()
              .vertical(true)
              .height(32)
    
            this.choiceText($r('app.string.match'), () => {
              bluetooth.setDevicePairingConfirmation(this.data.deviceId, true)
              logger.info(TAG, `setDevicePairingConfirmation = ${bluetooth.setDevicePairingConfirmation(this.data.deviceId, true)}`)
              this.controller.close()
            })
          }
          .margin({ top: 20 })
        }
        .width('100%')
        .padding(15)
      }
    ...
    
    //pages/index.ets
    @Entry
    @Component
    struct Index {
     build() {
        Column() {
          TitleBar({ handlerClickButton: this.handlerClickButton })
          Scroll() {
            Column() {
              Row() {
                //蓝牙开关
                Column() {
                  Text($r('app.string.bluetooth'))
                    .fontSize(30)
                    .margin({ top: 20 })
                    .alignSelf(ItemAlign.Start)
                  if (true === this.isOn) {
                    Text($r('app.string.discovery'))
                      .fontSize(20)
                      .alignSelf(ItemAlign.Start)
                  }
                }
    
                Blank()
    
                Column() {
                  Toggle({ type: ToggleType.Switch, isOn: this.isOn })
                    .selectedColor('#ff2982ea')
                    .onChange((isOn: boolean) => {
                      if (isOn) {
                        this.isOn = true
                        this.initBluetooth()
                      } else {
                        this.isOn = false
                        bluetooth.disableBluetooth()
                        this.deviceList = []
                        this.discoveryList = []
                      }
                    })
                }
                .id('toggleBtn')
              }
              .width('90%')
    
              if (this.isOn) {
                Divider()
                  .vertical(false)
                  .strokeWidth(10)
                  .color('#ffece7e7')
                  .lineCap(LineCapStyle.Butt)
                  .margin('1%')
                //已配对的设备
                Text($r('app.string.paired_device'))
                  .fontSize(25)
                  .fontColor('#ff565555')
                  .margin({ left: '5%' })
                  .alignSelf(ItemAlign.Start)
    
                ForEach(this.deviceList, (item, index) => {
                  Row() {
                    Text(item)
                      .fontSize(20)
                  }
                  .alignSelf(ItemAlign.Start)
                  .width('100%')
                  .height(50)
                  .margin({ left: '5%', top: '1%' })
                  .id(`pairedDevice${index}`)
                  .onClick(() => {
                    AlertDialog.show({
                      //取消配对
                      title: $r('app.string.disconnect'),
                      message: '此操作将会断开您与以下设备的连接:' + item,
                      primaryButton: {
                        value: $r('app.string.cancel'),
                        action: () => {
                        }
                      },
                      //确认取消
                      secondaryButton: {
                        value: $r('app.string.confirm'),
                        action: () => {
                          try {
                            bluetooth.cancelPairedDevice(item);
                            this.deviceList = bluetooth.getPairedDevices()
                            this.discoveryList = []
                            bluetooth.startBluetoothDiscovery()
                          } catch (err) {
                            console.error("errCode:" + err.code + ",errMessage:" + err.message);
                          }
                        }
                      }
                    })
                  })
                })
    
                Divider()
                  .vertical(false)
                  .strokeWidth(10)
                  .color('#ffece7e7')
                  .lineCap(LineCapStyle.Butt)
                  .margin('1%')
    
                Text($r('app.string.available_device'))
                  .fontSize(25)
                  .fontColor('#ff565555')
                  .margin({ left: '5%', bottom: '2%' })
                  .alignSelf(ItemAlign.Start)
                //可用设备列表
                ForEach(this.discoveryList, (item) => {
                  Row() {
                    Text(item)
                      .fontSize(20)
                  }
                  .alignSelf(ItemAlign.Start)
                  .width('100%')
                  .height(50)
                  .margin({ left: '5%', top: '1%' })
                  //进行配对操作点击
                  .onClick(() => {
                    logger.info(TAG, `start bluetooth.pairDevice,item = ${item}`)
                    let pairStatus = bluetooth.pairDevice(item)
                    logger.info(TAG, `pairStatus = ${pairStatus}`)
                  })
    
                  Divider()
                    .vertical(false)
                    .color('#ffece7e7')
                    .lineCap(LineCapStyle.Butt)
                    .margin('1%')
                })
              }
            }
          }
          .constraintSize({ maxHeight: '85%' })
        }
      }
    }

  3. 蓝牙开关打开关闭操作,需要打开蓝牙开关才能进行后续操作:

    initBluetooth() {
        bluetooth.on('stateChange', (data) => {
          logger.info(TAG, `enter on stateChange`)
          //判断蓝牙开关状态
          if (data === bluetooth.BluetoothState.STATE_ON) {
            logger.info(TAG, `enter BluetoothState.STATE_ON`)
            //蓝牙打开后的相关操作
            this.foundDevices()
          }
          if (data === bluetooth.BluetoothState.STATE_OFF) {
            logger.info(TAG, `enter BluetoothState.STATE_OFF`)
            bluetooth.off('bluetoothDeviceFind', (data) => {
              logger.info(TAG, `offBluetoothDeviceFindData = ${JSON.stringify(data)}`)
            })
            //关闭
            bluetooth.stopBluetoothDiscovery()
            this.discoveryList = []
          }
          logger.info(TAG, `BluetoothState = ${JSON.stringify(data)}`)
        })
        //开启蓝牙
        bluetooth.enableBluetooth()
      }

  4. 设置蓝牙扫描模式并开启扫描去发现设备,并订阅蓝牙设备发现上报时间获取设备列表

     foundDevices() {
        //订阅蓝牙设备发现上报事件
        bluetooth.on('bluetoothDeviceFind', (data) => {
          logger.info(TAG, `enter on bluetoothDeviceFind`)
          if (data !== null && data.length > 0) {
            if (this.discoveryList.indexOf(data[0]) === -1 && this.deviceList.indexOf(data[0]) === -1) {
              this.discoveryList.push(data[0])
            }
            logger.info(TAG, `discoveryList = ${JSON.stringify(this.discoveryList)}`)
          }
          let list = bluetooth.getPairedDevices()
          if (list !== null && list.length > 0) {
            this.deviceList = list
            logger.info(TAG, `deviceList =  ${JSON.stringify(this.deviceList)}`)
          }
        })
        //开启蓝牙扫描,可以发现远端设备
        bluetooth.startBluetoothDiscovery()
        //设置蓝牙扫描模式,可以被远端设备发现
        bluetooth.setBluetoothScanMode(bluetooth.ScanMode.SCAN_MODE_CONNECTABLE_GENERAL_DISCOVERABLE, TIME)
      }

  5. 设置蓝牙扫描模式并开启扫描去发现设备,并订阅蓝牙设备发现上报时间获取设备列表

    //配对确定和取消代码在PinDialog.ets文件中
    //setDevicePairingConfirmation(device: string, accept: boolean): void
    //device	string	表示远端设备地址,例如:"XX:XX:XX:XX:XX:XX
    //accept	boolean	接受配对请求设置为true,否则设置为false
    
    //订阅蓝牙配对状态改变事件,根据蓝牙状态更新设备列表
    bluetooth.on('bondStateChange', (data) => {
        logger.info(TAG, `enter bondStateChange`)
        logger.info(TAG, `data = ${JSON.stringify(data)}`)
        if (data.state === bluetooth.BondState.BOND_STATE_BONDED) {
            logger.info(TAG, `BOND_STATE_BONDED`)
            let index = this.discoveryList.indexOf(data.deviceId)
            this.discoveryList.splice(index, 1)
            this.deviceList = bluetooth.getPairedDevices()
        }
        if (data.state === bluetooth.BondState.BOND_STATE_INVALID) {
            logger.info(TAG, `BOND_STATE_INVALID`)
            this.deviceList = bluetooth.getPairedDevices()
        }
        logger.info(TAG, `bondStateChange,data = ${JSON.stringify(data)}`)
    })


最后

有很多小伙伴不知道学习哪些鸿蒙开发技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?但是又不知道从哪里下手,而且学习时频繁踩坑,最终浪费大量时间。所以本人整理了一些比较合适的鸿蒙(HarmonyOS NEXT)学习路径和一些资料的整理供小伙伴学习

点击领取→纯血鸿蒙Next全套最新学习资料(安全链接,放心点击

希望这一份鸿蒙学习资料能够给大家带来帮助,有需要的小伙伴自行领取,限时开源,先到先得~无套路领取!!

一、鸿蒙(HarmonyOS NEXT)最新学习路线

有了路线图,怎么能没有学习资料呢,小编也准备了一份联合鸿蒙官方发布笔记整理收纳的一套系统性的鸿蒙(OpenHarmony )学习手册(共计1236页)与鸿蒙(OpenHarmony )开发入门教学视频,内容包含:(ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、Harmony南向开发、鸿蒙项目实战等等)鸿蒙(HarmonyOS NEXT)…等技术知识点。

获取以上完整版高清学习路线,请点击→纯血版全套鸿蒙HarmonyOS学习资料

二、HarmonyOS Next 最新全套视频教程

三、《鸿蒙 (OpenHarmony)开发基础到实战手册》

OpenHarmony北向、南向开发环境搭建

《鸿蒙开发基础》

  • ArkTS语言
  • 安装DevEco Studio
  • 运用你的第一个ArkTS应用
  • ArkUI声明式UI开发
  • .……

《鸿蒙开发进阶》

  • Stage模型入门
  • 网络管理
  • 数据管理
  • 电话服务
  • 分布式应用开发
  • 通知与窗口管理
  • 多媒体技术
  • 安全技能
  • 任务管理
  • WebGL
  • 国际化开发
  • 应用测试
  • DFX面向未来设计
  • 鸿蒙系统移植和裁剪定制
  • ……

《鸿蒙进阶实战》

  • ArkTS实践
  • UIAbility应用
  • 网络案例
  • ……

四、大厂面试必问面试题

五、鸿蒙南向开发技术

六、鸿蒙APP开发必备

七、鸿蒙生态应用开发白皮书V2.0PDF


完整鸿蒙HarmonyOS学习资料,请点击→纯血版全套鸿蒙HarmonyOS学习资料

总结
总的来说,华为鸿蒙不再兼容安卓,对中年程序员来说是一个挑战,也是一个机会。只有积极应对变化,不断学习和提升自己,他们才能在这个变革的时代中立于不败之地。 

                        

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1869392.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

《Nest系列 - 4. 听说人人都会CRUD,可是我还不会怎么办???-《4.3结合前端使用实现多表联合查询》

🍑 联合查询 在我们前端来说,会抽离一些公用组件。不会把重复的组件或者所有代码都放在一个文件夹下。对于后端来说,也是一样的, 我们不会把所有数据都放在一张表里,我们回进行分表,根据一些关联关系&…

为什么说展厅数字人是展览未来的趋势?

展厅数字人是利用数字化、智能化和网络化等信息技术手段提升展厅展览服务和游览体验的全新载体。随着人工智能和虚拟现实技术的应用发展,展厅数字人已成为展厅展览转型升级的重要趋势。 展厅数字人凭借其创新性、强可塑性,成为展厅新名片,为各…

数据库工具之 —— SQLite

数据库工具之 —— SQLite SQLite 是一个非常流行的轻量级数据库,它是一个嵌入式的数据库,意味着数据库文件是存储在磁盘上的一个单一文件。SQLite 不需要一个独立的服务器进程,这使得它非常适合用于小型应用、移动应用、桌面应用&#xff0…

阿里云centos7.9 挂载数据盘 并更改宝塔站点根目录

一、让系统显示中文 参考:centos7 怎么让命令行显示中文(英文->中文)_如何在命令行中显示中文-CSDN博客 1、输入命令:locale -a |grep "zh_CN" 可以看到已经存在了中文包 2、输入命令:sudo vi…

Django REST framework安全实践:轻松实现认证、权限与限流功能

系列文章目录 Django入门全攻略:从零搭建你的第一个Web项目Django ORM入门指南:从概念到实践,掌握模型创建、迁移与视图操作Django ORM实战:模型字段与元选项配置,以及链式过滤与QF查询详解Django ORM深度游&#xff1…

ROT5、ROT13、ROT18、ROT47全系列加解密小程序

ROT5、ROT13、ROT18、ROT47全系列加解密小程序 这几天在看CTF相关的课程,涉及到古典密码学和近代密码学还有现代密码学。自己编了一个关于ROT全系列的加、解密小程序。 ​ ROT5、ROT13、ROT18、ROT47 编码是一种简单的码元位置顺序替换暗码。此类编码具有可逆性&a…

use embeddings stored in vector db to reduce work for LLM generating response

题意:使用存储在向量数据库中的嵌入来表示,以减少大型语言模型(LLM)生成响应的工作量。 问题背景: Im trying to understand what the correct strategy is for storing and using embeddings in a vector database, …

计算机网路面试HTTP篇三

HTTPS RSA 握手解析 我前面讲,简单给大家介绍了的 HTTPS 握手过程,但是还不够细! 只讲了比较基础的部分,所以这次我们再来深入一下 HTTPS,用实战抓包的方式,带大家再来窥探一次 HTTPS。 对于还不知道对称…

海报在线制作系统源码小程序

轻松设计,创意无限 一款基于ThinkPHPFastAdminUniApp开发的海报在线制作系统, 本系统不包含演示站中的素材模板资源。​ 一、引言:设计新纪元,在线海报制作引领潮流 在数字时代,海报已成为传播信息、展示创意的重要媒…

松下的台灯值得入手吗?书客、飞利浦热门品牌横评分享!

自从儿子步入小学,他埋首于书桌前的时光愈发冗长,很欣慰他能够认真专心学习,却也隐隐担忧他的视力健康。在了解视力健康中发现长时间在过暗或过亮的光线环境下学习,会导致瞳孔频繁地收缩与扩张,极易引发视觉疲劳。更令…

Isaac Sim 9 物理(1)

使用Python USD API 来实现 Physics 。 以下内容中,大部分 Python 代码可以在 Physics Python 演示脚本文件中找到,本文仅作为个人学习笔记。 一.设置 USD Stage 和物理场景 Setting up a USD Stage and a Physics Scene USD Stage不知道怎么翻译&#…

开放式耳机怎么选?2024全价位段机型推荐,真人实测不踩雷

作为一位深度测评耳机的专家,我深知对于音乐制作人员而言,优选一副适宜的耳机是至关重要的。我的一位朋友,身为音乐编辑,常常需长时间戴着耳机进行音频编辑与混音。他尝试了众多开放式耳机后,仍未找到完美契合的款式。…

使用 go-control-plane 自定义服务网格控制面

写在前面 阅读本文需要最起码了解envoy相关的概念 本文只是一个类似于demo的测试,只为了学习istio,更好的理解istio中的控制面和数据面(pilot -> proxy)是如何交互的,下图的蓝色虚线 先说go-control-plane是什么…

安宝特方案 | AR术者培养:AR眼镜如何帮助医生从“看”到“做”?

每一种新药品的上市都需要通过大量的临床试验,而每一种新的手术工具在普及使用之前也需要经过反复的实践和验证。医疗器械公司都面临着这样的挑战:如何促使保守谨慎的医生从仅仅观察新工具在手术中的应用,转变为在实际手术中实操这项工具。安…

简化收支记录,只留关键日期! 一键掌握财务流动,高效管理您的每一笔收支

在繁忙的生活中,管理个人或家庭的财务收支变得尤为重要。然而,传统的记账方式往往繁琐且复杂,让人望而却步。今天,我们为您推荐一款简洁易用的记账神器——晨曦记账本,让您轻松记录收支,只显示日期&#xf…

三种三相交流电动机正反转互锁电路的分析

PLC和固态继电器应用都很普及了,常规电磁继电器还有用武之地吗?答案是:有用武之地的。因为微处理器的应用使逻辑控制发生了变革,极大地发挥了开关功能的特性,但在应用中,它还是无法承受较大的负载,因此还要…

PyTorch的环境配置和安装

PyTorch环境配置及安装 初步机器学习,这里记录下一些学习经过,之后以便于自己查看,同时欢迎各位大佬点评,本节是机器计算的一个包的安装和简单验证。 安装、使用环境 Windows环境下:CUDA官网使用IDM下载就很快乐&am…

3 滑动窗口

滑动窗口是一种常用的数据结构和算法思想,广泛应用于处理数组或序列中的连续片段问题。它的核心特点是窗口的大小可以动态调整,但总保持一个固定大小,通过在序列上“滑动”来检查不同的子序列。以下是滑动窗口的一些典型应用场景:…

帝国cms未审核文章可视化预览效果

有时候为了让编辑更加清楚的看到别人审核之后的效果,同时文章有需要下一级审核才能在前端展示出来,今天就来展示一个未审核文章预览审核后的效果 这次给某出版社开发的时候,他们需要实现编辑能够预览自己发布之后的审核效果,所以就…

想学gis开发,java和c++那个比较好?

ava与C的应用场景不同,究竟选择谁,应该由开发者的兴趣方向来决定。 你选择Java,意味着以后的业务方向就是偏后台服务开发,如果你非得说我用java也可以写界面,对不起,别人不会。 刚好我有一些资料&#xf…