wolfplus 1 year ago
parent
commit
eb3b92a291
  1. 10
      local_plugins/ble_service/ios/Classes/BleAccessorySetup.swift
  2. 287
      local_plugins/ble_service/ios/Classes/BleService.swift

10
local_plugins/ble_service/ios/Classes/BleAccessorySetup.swift

@ -43,6 +43,14 @@ class BleAccessorySetup: NSObject {
service.connectToAccessory(uuid: uuid)
}
func addToReconnectQueue(uuid: UUID) {
guard let service = bleService else {
return
}
service.addToReconnectQueue(uuid: uuid)
}
// 从视图控制器显示设备选择器 - 主要入口点
func showDevicePickerFromViewController(_ viewController: UIViewController) {
initializeSession()
@ -113,9 +121,11 @@ class BleAccessorySetup: NSObject {
private func handleSessionEvent(_ event: ASAccessoryEvent) {
switch event.eventType {
case .activated:
os_log("会话已激活", log: logger, type: .info)
handleSessionActivated()
case .accessoryAdded:
os_log("发现新设备: %{public}@", log: logger, type: .info, event.accessory?.bluetoothIdentifier?.uuidString ?? "Unknown")
handleAccessoryAdded(event.accessory)
case .accessoryRemoved:

287
local_plugins/ble_service/ios/Classes/BleService.swift

@ -3,6 +3,15 @@ import CoreBluetooth
import os.log
import AVFoundation
/// BLE服务类 - 专为AccessorySetupKit设备设计,支持自动重连和状态恢复
///
/// 核心特性:
/// 1. 外设掉电→上电后自动重连(通过pendingReconnectUUIDs队列)
/// 2. App被系统终止后,外设广播或特征通知时自动恢复连接
/// 3. 使用CBCentralManagerOptionRestoreIdentifierKey实现系统级状态保留
/// 4. 支持bluetooth-central后台模式,允许后台BLE操作
/// 5. willRestoreState恢复系统保留的外设、扫描条件
/// 6. 统一的重连队列管理,适配AccessorySetupKit工作流程
@available(iOS 13.0, *)
public class BleService: NSObject {
// MARK: - 单例实现
@ -37,15 +46,13 @@ public class BleService: NSObject {
private var callbacks = [Callback]()
private var connectionState = BleConst.STATE_DISCONNECTED
// 待连接设备UUID队列
private var pendingUUIDs = [UUID]()
// 连接超时检测
private var connectionTimeoutTimer: Timer?
private let connectionTimeoutSeconds: TimeInterval = 15 // 15秒连接超时
// 状态恢复相关
private var restoredPeripherals: [CBPeripheral] = []
// 需要重置的设备(从willRestoreState记录,在poweredOn时重置)
private var devicesToReset: [CBPeripheral] = []
// 自动重连队列(统一管理所有待连接的设备)
private var pendingReconnectUUIDs = Set<UUID>()
private let logger = OSLog(subsystem: "com.yunqiinnovation.ble_service", category: "BleService")
@ -200,34 +207,58 @@ public class BleService: NSObject {
/// 连接到指定设备
public func connect(to peripheral: CBPeripheral) {
if connectionState == BleConst.STATE_CONNECTED || connectionState == BleConst.STATE_CONNECTING {
connectPeripheral(peripheral)
}
/// 统一的连接方法,支持恢复和重连
private func connectPeripheral(_ peripheral: CBPeripheral) {
guard let manager = centralManager else {
return
}
if centralManager == nil {
initializeCentralManager()
if peripheral.state == .connected {
os_log("设备已连接,恢复会话: %@", log: logger, type: .info, peripheral.identifier.uuidString)
resumeSession(for: peripheral)
return
}
guard let manager = centralManager else {
if peripheral.state == .connecting {
os_log("设备正在连接中: %@", log: logger, type: .info, peripheral.identifier.uuidString)
return
}
if connectionState == BleConst.STATE_CONNECTED && peripheralDevice?.identifier != peripheral.identifier {
os_log("断开当前设备,连接新设备", log: logger, type: .info)
disconnect()
}
updateConnectionState(BleConst.STATE_CONNECTING)
peripheralDevice = peripheral
peripheralDevice?.delegate = self
manager.connect(peripheral, options: [
CBConnectPeripheralOptionNotifyOnConnectionKey: true,
CBConnectPeripheralOptionNotifyOnDisconnectionKey: true,
CBConnectPeripheralOptionNotifyOnNotificationKey: true
])
// 启动连接超时检测
startConnectionTimeout()
os_log("开始连接到设备: %@", log: logger, type: .info, peripheral.identifier.uuidString)
}
/// 恢复已连接设备的会话
private func resumeSession(for peripheral: CBPeripheral) {
peripheralDevice = peripheral
peripheralDevice?.delegate = self
updateConnectionState(BleConst.STATE_CONNECTED)
if opusProcessor?.isDecoding() != true {
startOpusDecoding()
}
os_log("已恢复设备会话: %@", log: logger, type: .info, peripheral.identifier.uuidString)
}
/// 断开当前连接
public func disconnect() {
guard let peripheral = peripheralDevice,
@ -319,10 +350,6 @@ public class BleService: NSObject {
/// 打开编码器
func openEncoder() -> Bool {
os_log("🎙️ 准备打开编码(设备侧)...", log: logger, type: .info)
os_log("🔍 状态检查: 连接状态=%d (期望=%d), writeCharacteristic=%@",
log: logger, type: .debug,
connectionState, BleConst.STATE_CONNECTED,
writeCharacteristic != nil ? "已设置" : "未设置")
let paramData = Data([BleConst.CODEC_CONTROL_ENCODE_ON, BleConst.AUDIO_CHANNEL_RIGHT])
let result = sendCommand(BleConst.CMD_CONTROL_CODEC, data: paramData)
@ -404,34 +431,6 @@ public class BleService: NSObject {
return sendCommand(BleConst.CMD_CONTROL_CODEC, data: paramData)
}
// MARK: - 连接超时检测
/// 启动连接超时检测
private func startConnectionTimeout() {
stopConnectionTimeout() // 先停止现有的检测
connectionTimeoutTimer = Timer.scheduledTimer(withTimeInterval: connectionTimeoutSeconds, repeats: false) { [weak self] _ in
guard let self = self else { return }
os_log("⏰ 连接超时,取消连接", log: self.logger, type: .debug)
// 取消连接
if let peripheral = self.peripheralDevice {
self.centralManager?.cancelPeripheralConnection(peripheral)
}
self.updateConnectionState(BleConst.STATE_ERROR)
}
os_log("⏰ 连接超时检测已启动,%d秒后超时", log: logger, type: .debug, Int(connectionTimeoutSeconds))
}
/// 停止连接超时检测
private func stopConnectionTimeout() {
connectionTimeoutTimer?.invalidate()
connectionTimeoutTimer = nil
}
// MARK: - 私有方法
/// 更新连接状态并通知回调
@ -863,12 +862,30 @@ public class BleService: NSObject {
opusProcessor?.cleanup()
opusProcessor = nil
// 清理待重置设备列表
devicesToReset = []
// 清理状态恢复和重连队列
restoredPeripherals.removeAll()
pendingReconnectUUIDs.removeAll()
os_log("BleService资源已清理", log: logger, type: .debug)
}
/// 清除重连队列
public func clearReconnectQueue() {
pendingReconnectUUIDs.removeAll()
os_log("已清除重连队列", log: logger, type: .info)
}
/// 获取当前重连队列状态(用于调试)
public func getReconnectQueueStatus() -> [String: Any] {
return [
"reconnectQueueCount": pendingReconnectUUIDs.count,
"reconnectQueueUUIDs": Array(pendingReconnectUUIDs).map { $0.uuidString },
"restoredPeripheralsCount": restoredPeripherals.count,
"connectionState": connectionState,
"centralManagerState": centralManager?.state.rawValue ?? -1
]
}
// MARK: - AccessorySetupKit
/// 在接收到AccessorySetupKit的accessoryAdded事件后调用
@ -883,9 +900,8 @@ public class BleService: NSObject {
initializeCentralManager()
}
// 将设备添加到连接队列,而不是直接连接
// 这样即使当前蓝牙状态不是poweredOn,也会在状态变为poweredOn时自动连接
enqueueConnection(uuid: uuid)
// 添加到重连队列
addToReconnectQueue(uuid: uuid)
}
#endif
}
@ -908,27 +924,46 @@ extension BleService: CBCentralManagerDelegate {
switch central.state {
case .poweredOn:
os_log("🔧 蓝牙已开启", log: logger, type: .info)
// 蓝牙已开启,可以开始扫描或连接
os_log("🔧 蓝牙已开启 - 这是全新初始化(非状态恢复)", log: logger, type: .info)
// 注:AccessorySetupKit应用不需要恢复传统BLE扫描
// 首先检查是否有需要重置的设备(来自willRestoreState)
if !devicesToReset.isEmpty {
os_log("🔄 蓝牙已就绪,开始重置%d个设备", log: logger, type: .info, devicesToReset.count)
// 恢复外设连接
if !restoredPeripherals.isEmpty {
os_log("🔄 恢复%d个外设连接", log: logger, type: .info, restoredPeripherals.count)
for peripheral in devicesToReset {
os_log("🔄 强制取消设备连接: %{public}@, 状态=%d", log: logger, type: .info,
peripheral.name ?? peripheral.identifier.uuidString, peripheral.state.rawValue)
central.cancelPeripheralConnection(peripheral)
for peripheral in restoredPeripherals {
peripheral.delegate = self
if peripheral.state == .connected {
os_log("🔄 外设已连接,恢复会话: %{public}@", log: logger, type: .info,
peripheral.identifier.uuidString)
resumeSession(for: peripheral)
} else {
os_log("🔄 外设未连接,开始重连: %{public}@, 状态=%d", log: logger, type: .info,
peripheral.identifier.uuidString, peripheral.state.rawValue)
connectPeripheral(peripheral)
}
}
// 清空重置列表
devicesToReset = []
os_log("🔄 设备重置完成,继续正常连接流程", log: logger, type: .info)
// 清空已处理的恢复外设
restoredPeripherals.removeAll()
}
// 恢复待重连的设备
if !pendingReconnectUUIDs.isEmpty {
os_log("🔄 尝试重连%d个待重连设备", log: logger, type: .info, pendingReconnectUUIDs.count)
let peripherals = central.retrievePeripherals(withIdentifiers: Array(pendingReconnectUUIDs))
for peripheral in peripherals {
os_log("🔄 重连设备: %{public}@", log: logger, type: .info, peripheral.identifier.uuidString)
connectPeripheral(peripheral)
pendingReconnectUUIDs.remove(peripheral.identifier)
}
}
// 检查是否有待连接的设备
tryConnectIfReady()
// 尝试重连队列中的设备
tryReconnectDevices()
case .poweredOff:
os_log("蓝牙已关闭", log: logger, type: .info)
@ -956,63 +991,35 @@ extension BleService: CBCentralManagerDelegate {
}
}
// MARK: - 连接队列处理
// MARK: - 重连管理
// 检查并尝试连接待连接设备
private func tryConnectIfReady() {
os_log("🔍 检查并尝试连接待连接设备", log: logger, type: .debug)
/// 添加设备到重连队列
public func addToReconnectQueue(uuid: UUID) {
pendingReconnectUUIDs.insert(uuid)
os_log("🔄 设备已加入重连队列: %{public}@", log: logger, type: .info, uuid.uuidString)
// 立即尝试连接(如果蓝牙已就绪)
tryReconnectDevices()
}
/// 尝试重连队列中的设备
private func tryReconnectDevices() {
guard let manager = centralManager, manager.state == .poweredOn else {
os_log("蓝牙未就绪,延迟连接", log: logger, type: .info)
os_log("🔄 蓝牙未就绪,延迟重连", log: logger, type: .debug)
return
}
// 防止并发修改
let uuidsToProcess = pendingUUIDs
// 过滤:保留未能成功连接的设备
pendingUUIDs = uuidsToProcess.filter { uuid in
let peripherals = manager.retrievePeripherals(withIdentifiers: [uuid])
if let peripheral = peripherals.first {
// 如果已经连接或正在连接,则不需要再次连接
if peripheral.state == .connected || peripheral.state == .connecting {
os_log("设备已连接或正在连接:state=%d,从队列中移除: %{public}@", log: logger, type: .info, peripheral.state.rawValue, peripheral.name ?? peripheral.identifier.uuidString)
return false // 从队列中移除
}
// 找到外设,尝试连接
os_log("从队列中连接设备: %{public}@", log: logger, type: .info,
peripheral.name ?? peripheral.identifier.uuidString)
// 开始连接
peripheralDevice = peripheral
peripheralDevice?.delegate = self
manager.connect(peripheral, options: [
CBConnectPeripheralOptionNotifyOnConnectionKey: true,
CBConnectPeripheralOptionNotifyOnDisconnectionKey: true,
CBConnectPeripheralOptionNotifyOnNotificationKey: true
])
// 启动连接超时检测
startConnectionTimeout()
return false // 连接后从队列中移除
}
// 找不到外设,保留在队列中
return true
}
}
// 添加设备到连接队列
public func enqueueConnection(uuid: UUID) {
if !pendingUUIDs.contains(uuid) {
pendingUUIDs.append(uuid)
os_log("设备已加入连接队列: %{public}@", log: logger, type: .info, uuid.uuidString)
guard !pendingReconnectUUIDs.isEmpty else {
return
}
// 立即尝试连接
tryConnectIfReady()
os_log("🔄 尝试重连%d个设备", log: logger, type: .info, pendingReconnectUUIDs.count)
let peripherals = manager.retrievePeripherals(withIdentifiers: Array(pendingReconnectUUIDs))
for peripheral in peripherals {
os_log("🔄 重连设备: %{public}@", log: logger, type: .info, peripheral.identifier.uuidString)
connectPeripheral(peripheral)
}
}
// 原有方法
@ -1042,8 +1049,8 @@ extension BleService: CBCentralManagerDelegate {
os_log("🔗 连接详情: UUID=%{public}@, 服务数=%d", log: logger, type: .debug,
peripheral.identifier.uuidString, peripheral.services?.count ?? 0)
// 停止连接超时检测
stopConnectionTimeout()
// 从重连队列中移除(如果存在)
pendingReconnectUUIDs.remove(peripheral.identifier)
updateConnectionState(BleConst.STATE_CONNECTED)
@ -1063,10 +1070,11 @@ extension BleService: CBCentralManagerDelegate {
peripheral.name ?? peripheral.identifier.uuidString, errorMsg)
os_log("❌ 失败详情: UUID=%{public}@", log: logger, type: .debug, peripheral.identifier.uuidString)
// 停止连接超时检测
stopConnectionTimeout()
updateConnectionState(BleConst.STATE_ERROR)
// 连接失败时,立即发起新的连接
os_log("🔄 连接失败,立即重新连接: %{public}@", log: logger, type: .info, peripheral.identifier.uuidString)
connectPeripheral(peripheral)
}
public func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
@ -1078,42 +1086,33 @@ extension BleService: CBCentralManagerDelegate {
peripheral.name ?? peripheral.identifier.uuidString)
}
// 停止连接超时检测
stopConnectionTimeout()
updateConnectionState(BleConst.STATE_DISCONNECTED)
// 无论是否意外断开,都立即发起新的连接
if central.state == .poweredOn {
os_log("🔄 立即重新连接设备: %{public}@", log: logger, type: .info, peripheral.identifier.uuidString)
connectPeripheral(peripheral)
} else {
os_log("🔄 蓝牙未就绪,添加到重连队列: %{public}@", log: logger, type: .info, peripheral.identifier.uuidString)
pendingReconnectUUIDs.insert(peripheral.identifier)
}
}
public func centralManager(_ central: CBCentralManager, willRestoreState dict: [String : Any]) {
os_log("🔄 ===== iOS系统正在恢复CBCentralManager状态 =====", log: logger, type: .info)
os_log("🔄 恢复数据: %{public}@", log: logger, type: .debug, dict)
// 检查是否有需要恢复的外设
if let peripherals = dict[CBCentralManagerRestoredStatePeripheralsKey] as? [CBPeripheral], !peripherals.isEmpty {
os_log("🔄 发现%d个需要恢复的设备,记录待重置(等待蓝牙poweredOn)", log: logger, type: .info, peripherals.count)
// 只记录需要重置的设备,不执行任何蓝牙操作(因为此时蓝牙可能还没有poweredOn)
devicesToReset = peripherals
// 恢复外设连接
if let peripherals = dict[CBCentralManagerRestoredStatePeripheralsKey] as? [CBPeripheral] {
restoredPeripherals = peripherals
os_log("🔄 恢复%d个外设连接", log: logger, type: .info, peripherals.count)
for peripheral in peripherals {
os_log("🔄 记录待重置设备: %{public}@, 状态=%d", log: logger, type: .debug,
os_log("🔄 恢复外设: %{public}@, 状态=%d", log: logger, type: .debug,
peripheral.name ?? peripheral.identifier.uuidString, peripheral.state.rawValue)
}
// 预先清理应用层状态
peripheralDevice = nil
writeCharacteristic = nil
notifyCharacteristic = nil
audioCharacteristic = nil
updateConnectionState(BleConst.STATE_DISCONNECTED)
os_log("🔄 应用层状态已清理,等待蓝牙poweredOn后执行设备重置", log: logger, type: .info)
} else {
os_log("🔄 没有需要恢复的设备", log: logger, type: .debug)
devicesToReset = []
}
// 不进行任何蓝牙操作,等待centralManagerDidUpdateState(.poweredOn)
}
}

Loading…
Cancel
Save