iOS BluetoothLE:CBCentralManager 取消订阅更新
iOS BluetoothLE: CBCentralManager Unsubscribes From Updates
我有一个 mac 设置为蓝牙配件,使用 CoreBluetooth 作为 CBPeripheralManager
。 Mac 在一组特征 CBUUID
上做广告,一旦它有订阅者,我就会点击一个按钮,每半秒播放一次 UTF-8 编码的时间戳。
我有一个 iPhone 设置为 CBCentralManager
订阅适当的特征。每次收到数据并且应用程序处于活动状态时,我都会使用解码的时间戳更新 iPhone 的 UI。 bluetooth-central
后台模式在 .plist
文件中设置。
iPhone 继续调试打印并更新时间戳的 UI 大约 25 秒,然后就停止了。无论应用程序是在前台还是在后台,都会发生这种情况。 编辑: CBPeripheralManager
此时收到一个 didUnsubscribeFrom characteristic
回调。我看不出有任何理由 didUnsubscribeFrom
会被调用,不知道为什么它总是在 25 秒后。
CBPeripheralManager
继续愉快地发送它的时间戳。来自 CBPeripheralManager
的 updateData(_:for:onSubscribedCentrals:)
调用的 return 值始终为真,表明队列永远不会满。
这里涉及的代码比较多,只展示最相关的。
来自 CBCentralManager
应用:
func centralManagerDidUpdateState(_ central: CBCentralManager) {
print(central.state.rawValue)
centralManager.scanForPeripherals(withServices: [timeUUID], options: nil)
print("scanning")
}
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
peripheral.delegate = self
peripheral.discoverServices(nil)
print("connected")
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
print(peripheral.name as Any)
print(advertisementData[CBAdvertisementDataServiceUUIDsKey] as! Array<CBUUID>)
self.peripheralController = peripheral
self.centralManager.connect(peripheral, options: nil)
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
if service.uuid.uuidString == timeUUID.uuidString {
peripheralController.setNotifyValue(true, for: service.characteristics!.first!)
peripheralController.readValue(for: service.characteristics!.first!) // EDIT: This was the offending line
}
}
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
if characteristic.uuid.uuidString == timeUUID.uuidString {
if let valueFrom = characteristic.value {
if let this = String(data: valueFrom, encoding: .utf8) {
if UIApplication.shared.applicationState == .active {
label.text = this
print("ACTIVE \(this)")
} else if UIApplication.shared.applicationState == .background {
print("BACKGROUND \(this)")
} else if UIApplication.shared.applicationState == .inactive {
print("INACTIVE \(this)")
}
}
}
}
}
来自 CBPeripheralManager
应用:
func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
myCharacteristic = CBMutableCharacteristic(type: myServiceUUID, properties: [CBCharacteristicProperties.read, CBCharacteristicProperties.notify], value: nil, permissions: CBAttributePermissions.readable)
let myService = CBMutableService(type: myServiceUUID, primary: true)
myService.characteristics = [myCharacteristic]
bluetoothController.add(myService)
}
func peripheralManager(_ peripheral: CBPeripheralManager, central: CBCentral, didSubscribeTo characteristic: CBCharacteristic) {
print(characteristic.uuid)
subscriber = central
}
func peripheralManager(_ peripheral: CBPeripheralManager, central: CBCentral, didUnsubscribeFrom characteristic: CBCharacteristic) {
print(characteristic)
print("unsubscribe")
}
func repeatAdvertisement() {
timer = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true) { [unowned self] (timerRef) in
guard let maybeTimer = self.timer, maybeTimer.isValid else { return }
let datum = Date()
let stringFromDate = self.dateFormatter.string(from: datum)
let data = stringFromDate.data(using: .utf8)
print(data!.count)
// myCharacteristic.value = data
let myService = CBMutableService(type: self.myServiceUUID, primary: true)
myService.characteristics = [self.myCharacteristic]
let did = self.bluetoothController.updateValue(data!, for: self.myCharacteristic as! CBMutableCharacteristic, onSubscribedCentrals: [self.subscriber])
print("timed \(stringFromDate) \(did)")
}
}
func advertise() {
if timer == nil {
repeatAdvertisement()
} else {
timer?.invalidate()
timer = nil
}
}
让我知道您还需要什么。
好的,看在上帝的份上。问题是行 peripheralController.readValue(for: service.characteristics!.first!)
我根据一些示例代码在应用程序中有该行,好吧,这是不必要的。
显然,对 readValue(for:)
的调用会导致某种超时。我从应用程序中编辑了该行,它愉快地不断更新。
留下问题并添加这个答案,以防有一天有人遇到同样的事情。
我有一个 mac 设置为蓝牙配件,使用 CoreBluetooth 作为 CBPeripheralManager
。 Mac 在一组特征 CBUUID
上做广告,一旦它有订阅者,我就会点击一个按钮,每半秒播放一次 UTF-8 编码的时间戳。
我有一个 iPhone 设置为 CBCentralManager
订阅适当的特征。每次收到数据并且应用程序处于活动状态时,我都会使用解码的时间戳更新 iPhone 的 UI。 bluetooth-central
后台模式在 .plist
文件中设置。
iPhone 继续调试打印并更新时间戳的 UI 大约 25 秒,然后就停止了。无论应用程序是在前台还是在后台,都会发生这种情况。 编辑: CBPeripheralManager
此时收到一个 didUnsubscribeFrom characteristic
回调。我看不出有任何理由 didUnsubscribeFrom
会被调用,不知道为什么它总是在 25 秒后。
CBPeripheralManager
继续愉快地发送它的时间戳。来自 CBPeripheralManager
的 updateData(_:for:onSubscribedCentrals:)
调用的 return 值始终为真,表明队列永远不会满。
这里涉及的代码比较多,只展示最相关的。
来自 CBCentralManager
应用:
func centralManagerDidUpdateState(_ central: CBCentralManager) {
print(central.state.rawValue)
centralManager.scanForPeripherals(withServices: [timeUUID], options: nil)
print("scanning")
}
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
peripheral.delegate = self
peripheral.discoverServices(nil)
print("connected")
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
print(peripheral.name as Any)
print(advertisementData[CBAdvertisementDataServiceUUIDsKey] as! Array<CBUUID>)
self.peripheralController = peripheral
self.centralManager.connect(peripheral, options: nil)
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
if service.uuid.uuidString == timeUUID.uuidString {
peripheralController.setNotifyValue(true, for: service.characteristics!.first!)
peripheralController.readValue(for: service.characteristics!.first!) // EDIT: This was the offending line
}
}
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
if characteristic.uuid.uuidString == timeUUID.uuidString {
if let valueFrom = characteristic.value {
if let this = String(data: valueFrom, encoding: .utf8) {
if UIApplication.shared.applicationState == .active {
label.text = this
print("ACTIVE \(this)")
} else if UIApplication.shared.applicationState == .background {
print("BACKGROUND \(this)")
} else if UIApplication.shared.applicationState == .inactive {
print("INACTIVE \(this)")
}
}
}
}
}
来自 CBPeripheralManager
应用:
func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
myCharacteristic = CBMutableCharacteristic(type: myServiceUUID, properties: [CBCharacteristicProperties.read, CBCharacteristicProperties.notify], value: nil, permissions: CBAttributePermissions.readable)
let myService = CBMutableService(type: myServiceUUID, primary: true)
myService.characteristics = [myCharacteristic]
bluetoothController.add(myService)
}
func peripheralManager(_ peripheral: CBPeripheralManager, central: CBCentral, didSubscribeTo characteristic: CBCharacteristic) {
print(characteristic.uuid)
subscriber = central
}
func peripheralManager(_ peripheral: CBPeripheralManager, central: CBCentral, didUnsubscribeFrom characteristic: CBCharacteristic) {
print(characteristic)
print("unsubscribe")
}
func repeatAdvertisement() {
timer = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true) { [unowned self] (timerRef) in
guard let maybeTimer = self.timer, maybeTimer.isValid else { return }
let datum = Date()
let stringFromDate = self.dateFormatter.string(from: datum)
let data = stringFromDate.data(using: .utf8)
print(data!.count)
// myCharacteristic.value = data
let myService = CBMutableService(type: self.myServiceUUID, primary: true)
myService.characteristics = [self.myCharacteristic]
let did = self.bluetoothController.updateValue(data!, for: self.myCharacteristic as! CBMutableCharacteristic, onSubscribedCentrals: [self.subscriber])
print("timed \(stringFromDate) \(did)")
}
}
func advertise() {
if timer == nil {
repeatAdvertisement()
} else {
timer?.invalidate()
timer = nil
}
}
让我知道您还需要什么。
好的,看在上帝的份上。问题是行 peripheralController.readValue(for: service.characteristics!.first!)
我根据一些示例代码在应用程序中有该行,好吧,这是不必要的。
显然,对 readValue(for:)
的调用会导致某种超时。我从应用程序中编辑了该行,它愉快地不断更新。
留下问题并添加这个答案,以防有一天有人遇到同样的事情。