Read the original article:Bluetooth Listener Callback Triggered Multiple Times
Problem Description
When subscribing to Bluetooth event listeners using the .on('XXX') interface (for example, on('stateChange')), the callback may sometimes be triggered multiple times even though the event is triggered only once.
Example: Bluetooth switch state listener.
import { access } from '@kit.ConnectivityKit';
@Entry
@Component
struct StateChangePage {
// Counter
@State number: number = 0;
on() {
// Subscribe to Bluetooth state change listener
access.on('stateChange', (callback: access.BluetoothState) => {
console.info(`${this.number} :Bluetooth State: ${callback}`)
this.number++
})
}
build() {
Column() {
Button('Start Listening').onClick(() => {
for (let i = 0; i < 2; i++) {
// Call the listener twice
this.on()
}
})
}
}
}
Observed Logs:
15:20:08.535 51966-51966 A03D00/com.example.ble/JSAPP com.example.ble I 0: Bluetooth State: 2
15:20:08.535 51966-51966 A03D00/com.example.ble/JSAPP com.example.ble I 1: Bluetooth State: 2
The callback triggers twice even though the state changes only once.
Background Knowledge
The .on('XXX') interface can be called multiple times to create multiple subscriptions.
When the listener is no longer needed, developers should explicitly call the corresponding .off('XXX') method to cancel the subscription.
For example: off('stateChange').
Troubleshooting Process
Use debugging or logging to verify whether .on('XXX') is being invoked more than once before adding the listener.
If multiple listeners are created, the callback will trigger repeatedly.
Solution
Solution 1:
Call the corresponding .off('XXX') interface when the listener is no longer needed to prevent stacking multiple listeners.
Example: Bluetooth State Change Listener
import { access } from '@kit.ConnectivityKit';
@Entry
@Component
struct StateChangePage {
@State number: number = 0;
on() {
console.info('Start Listening')
access.on('stateChange', (callback: access.BluetoothState) => {
console.info(`${this.number} :Bluetooth State: ${callback}`)
this.number++
})
}
build() {
Column() {
Button('Start Listening').onClick(() => {
this.on()
})
Button('Stop Listening').onClick(() => {
console.info('Stop Listening')
access.off('stateChange')
})
Button('Enable Bluetooth').onClick(() => {
if (access.getState() === 0) {
console.info('Enable Bluetooth')
access.enableBluetooth()
}
})
Button('Disable Bluetooth').onClick(() => {
if (access.getState() === 2) {
console.info('Disable Bluetooth')
access.disableBluetooth()
}
})
}
}
}
Execution Result:
Solution 2:
Before calling .on('XXX'), proactively call .off('XXX') once to ensure previously created listeners are cleared.
import { access } from '@kit.ConnectivityKit';
@Entry
@Component
struct StateChangePage {
@State number: number = 0;
on() {
console.info('Start Listening')
access.on('stateChange', (callback: access.BluetoothState) => {
console.info(`${this.number} :Bluetooth State: ${callback}`)
this.number++
})
}
build() {
Column() {
Button('Start Listening').onClick(() => {
// Clear any previous Bluetooth listeners before adding a new one
access.off('stateChange')
console.info('Cleared previous listener')
this.on()
})
Button('Enable Bluetooth').onClick(() => {
if (access.getState() === 0) {
console.info('Enable Bluetooth')
access.enableBluetooth()
}
})
Button('Disable Bluetooth').onClick(() => {
if (access.getState() === 2) {
console.info('Disable Bluetooth')
access.disableBluetooth()
}
})
}
}
}
Execution Result:
Verification Result
- Verified that duplicate callbacks stop occurring after applying either solution.
-
.off('stateChange')effectively removes previously created listeners. - Repeated creation of
.on('XXX')without corresponding.off()confirmed as root cause. - Supports API Version 19 Release and above.
- Requires HarmonyOS 5.1.1 Release SDK or later.
- Must be compiled and executed in DevEco Studio 5.1.1 Release or later.
Code Check Cleared Screenshot:



Top comments (0)