# 蓝牙监听接口重复触发

#### 问题现象

在使用蓝牙access.on('stateChange')类接口订阅事件监听时，有时候会出现发起一次事件请求，监听接口回调重复触发的问题发生。

#### 背景知识

同一个access.on('stateChange')接口允许被多次创建，当access.on('stateChange')接口不再需要使用时，可调用相应的access.off('stateChange')接口取消监听。

#### 问题定位

可通过断点调试，或者在调用access.on('stateChange')接口前添加一条日志输出，排查接口是否被多次调用。

#### 分析结论

只有当access.on('stateChange')接口被多次创建时，才会出现接口回调重复触发的问题。

#### 解决方案

access.on('stateChange')接口不再需要使用时，请及时调用相配套的access.off('stateChange')接口取消监听，避免下一次调用access.on('stateChange')接口时，之前调用的access.on('stateChange')接口没有被取消监听。以蓝牙开关状态监听接口为例：

```
import { access } from '@kit.ConnectivityKit';
import { abilityAccessCtrl, common, PermissionRequestResult } from '@kit.AbilityKit';

@Entry
@Component
struct StateChangePage {
  // 计数器
  @State number: number = 0;

  aboutToAppear(): void {
    let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
    atManager.requestPermissionsFromUser(this.getUIContext()?.getHostContext() as common.UIAbilityContext,
      ['ohos.permission.ACCESS_BLUETOOTH'], (err, data: PermissionRequestResult) => {
        if (err) {
          console.error(`requestPermissionsFromUser fail, err->${JSON.stringify(err)}`);
        } else {
          console.info(`data:${JSON.stringify(data)}`);
        }
      });
  }

  on() {
    console.info('开启监听');
    // 订阅蓝牙开关状态监听
    try {
      access.on('stateChange', (callback: access.BluetoothState) => {
        console.info(`${this.number} :蓝牙开关状态： ${callback} `);
        this.number++;
      });
    } catch (error) {
      console.info(`开启监听error:${error}`);
    }
  }

  build() {
    Column() {
      Button('开启监听').onClick(() => {
        this.on();
      }).margin(15);
      Button('关闭监听').onClick(() => {
        console.info('关闭监听');
        try {
          access.off('stateChange');
        } catch (error) {
          console.info(`关闭监听error:${error}`);
        }
      }).margin(15);
      Button('开启蓝牙').onClick(() => {
        try {
          if (access.getState() === 0) {
            console.info('开启蓝牙');
            access.enableBluetooth();
          }
        } catch (error) {
          console.info(`开启蓝牙error:${error}`);
        }
      }).margin(15);
      Button('关闭蓝牙').onClick(() => {
        try {
          if (access.getState() === 2) {
            console.info('关闭蓝牙');
            access.disableBluetooth();
          }
        } catch (error) {
          console.info(`关闭蓝牙error:${error}`);
        }
      });
    }.height('100%')
    .width('100%')
    .justifyContent(FlexAlign.SpaceAround);
  }
}
```

使用蓝牙能力时，需要在module.json5中添加ohos.permission.ACCESS\_BLUETOOTH权限。

#### 常见FAQ

Q：BLE蓝牙连接成功之后进行MTU协商，并调用on('BLEMtuChange')方法监听MTU变化，但是调用off('BLEMtuChange')方法取消监听时回调一直不执行，这是什么原因？

A：取消监听off('BLEMtuChange')方法不会有回调，传入的参数是计划要取消的回调函数。

Q：为什么取消订阅蓝牙设备开关状态事件access.off('stateChange')方法可以正常取消监听，而加上第二个参数Callback后无法取消监听？

A：取消订阅蓝牙设备开关状态事件access.off('stateChange')传入的回调，需要和订阅on的回调函数是同一个函数。若回调传的是匿名函数，与订阅access.on('stateChange')的回调函数不一致，将无法取消监听。

Q：access.on('stateChange')能否监听蓝牙权限状态变化？

A：access.on('stateChange')：用于监听“设置-蓝牙”中蓝牙开启/关闭状态变化，不能监听“应用和元服务-应用”下的应用详情中的权限开启/关闭状态变化。