Flutter iOS应用集成Polar BLE SDK 5.11.0+调用startAutoConnectToDevice时发生死锁
Flutter iOS应用集成Polar BLE SDK 5.11.0+调用startAutoConnectToDevice时发生死锁
我最近在开发一款基于Flutter的iOS心率监测应用,集成Polar BLE SDK时遇到了一个棘手的死锁问题:SDK从5.10.0升级到5.11.0及以上版本(包括最新的6.5.0)后,调用startAutoConnectToDevice方法会导致应用完全冻结,而5.10.0版本则一切正常。下面是完整的问题分析和解决方案:
环境信息
- Flutter版本:3.35.2+
- iOS版本:17.x
- Polar BLE SDK版本:5.11.0+(5.10.0可正常运行)
- 测试设备:iPhone实体机
- 心率监测硬件:Polar/Wahoo TICKR
最小复现代码
Flutter端代码(main.dart)
import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; void main() => runApp(const MyApp()); class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( home: MyHomePage(), ); } } class MyHomePage extends StatelessWidget { static const platform = MethodChannel('com.example.app/polar'); @override Widget build(BuildContext context) { return Scaffold( body: Center( child: TextButton( onPressed: () { platform.invokeMethod('autoConnectToDevice'); }, child: const Text('Connect'), ), ), ); } }
iOS端代码
HrService.swift
import PolarBleSdk import RxSwift import CoreBluetooth class HrService: PolarBleApiObserver { var api: PolarBleApi? = nil private var autoConnectDisposable: Disposable? func initializeApi() { api = PolarBleApiDefaultImpl.polarImplementation(DispatchQueue.main, features: []) api!.observer = self } func deviceConnecting(_ identifier: PolarDeviceInfo) { NSLog("deviceConnecting") } func deviceConnected(_ identifier: PolarDeviceInfo) { NSLog("deviceConnected") } func deviceDisconnected(_ identifier: PolarDeviceInfo, pairingError: Bool) { NSLog("deviceDisconnected") } func autoConnectToDevice(result: @escaping FlutterResult) { guard let api = api else { return result(FlutterError(code: "polarApiNil", message: nil, details: nil)) } autoConnectDisposable?.dispose() autoConnectDisposable = api.startAutoConnectToDevice(-80, service: CBUUID(string: "180D"), polarDeviceType: nil) .subscribe { e in switch e { case .completed: result(nil) case .error(_): result(FlutterError()) @unknown default: break } } } }
AppDelegate.swift
@main @objc class AppDelegate: FlutterAppDelegate { let POLAR_METHOD_CHANNEL = "com.example.app/polar" let hrService = HrService() override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { let controller = window?.rootViewController as! FlutterViewController let polarMethodChannel = FlutterMethodChannel(name: POLAR_METHOD_CHANNEL, binaryMessenger: controller.binaryMessenger) hrService.initializeApi() polarMethodChannel.setMethodCallHandler({ (call: FlutterMethodCall, result: @escaping FlutterResult) in if call.method == "autoConnectToDevice" { self.hrService.autoConnectToDevice(result: result) } }) GeneratedPluginRegistrant.register(with: self) return super.application(application, didFinishLaunchingWithOptions: launchOptions) } }
死锁现象与栈追踪
点击"Connect"按钮后应用完全无响应,主线程栈追踪显示死锁发生在AtomicType的锁操作上:
Thread 1 Queue : com.apple.main-thread (serial) #0 0x00000001e2126628 in __psynch_mutexwait () #1 0x00000001fcc6ac94 in _pthread_mutex_firstfit_lock_wait () #2 0x00000001fcc69cac in _pthread_mutex_firstfit_lock_slow () #3 0x0000000102ba25b0 in AtomicType.accessItem(_:) at .../AtomicType.swift:26 #4 0x000000010669fcbc in CBScanner.scanningNeeded() at .../CBScanner.swift:133 #5 0x0000000106686170 in CBDeviceListenerImpl.updateSessionState(_:state:) #6 0x000000010668cd80 in CBDeviceListenerImpl.openSessionDirect(_:) #7 0x000000010681f248 in closure #2 in PolarBleApiImpl.startAutoConnectToDevice(_:service:polarDeviceType:) ... #29 0x0000000102ba25f4 in AtomicType.accessItem(_:) at .../AtomicType.swift:27 #30 0x0000000102c10e08 in closure #3 in CBDeviceListenerImpl.handleDeviceDiscovered(_:didDiscover:advertisementData:rssi:)
根因分析
对比Polar BLE SDK 5.10.0和5.11.0的代码,发现CBScanner.swift中的scanObservers变量实现发生了变更,目的是提升线程安全性,但引入了死锁风险:
// 5.10.0版本实现 var scanObservers = Set<RxObserver<BleDeviceSession>>() func addClient(_ scanner: RxObserver<BleDeviceSession>){ scanObservers.insert(scanner) self.commandState(ScanAction.clientStartScan) } // 5.11.0+版本实现 var scanObservers = AtomicType(initialValue: Set<RxObserver<BleDeviceSession>>()) func addClient(_ scanner: RxObserver<BleDeviceSession>){ scanObservers.accessItem { $0.insert(scanner) } self.commandState(ScanAction.clientStartScan) }
死锁的核心原因是:主线程在调用startAutoConnectToDevice时已经持有了AtomicType的锁,后续的SDK内部回调又在同一个主线程中再次尝试获取该锁,导致线程自身死锁。
已尝试的无效方案
- 更换Polar API初始化队列:试过主线程、后台队列、自定义串行队列
- 异步包装调用:将
startAutoConnectToDevice包裹在各种DispatchQueue.async中执行 - 延迟执行:使用
DispatchQueue.main.asyncAfter延迟调用接口
有效解决方案
问题的关键是避免在同一个线程中嵌套获取锁,我们可以通过将Polar SDK的核心操作切换到独立的串行队列来打破死锁链:
修改iOS端HrService.swift代码
- 创建独立的串行队列,避免与主线程共享:
private let polarQueue = DispatchQueue(label: "com.example.app.polar.queue", qos: .utility)
- 初始化Polar API时使用该独立队列:
func initializeApi() { api = PolarBleApiDefaultImpl.polarImplementation(polarQueue, features: []) api!.observer = self }
- 在独立队列中执行
startAutoConnectToDevice,并将结果回调切回主线程(符合Flutter Method Channel要求):
func autoConnectToDevice(result: @escaping FlutterResult) { guard let api = api else { return result(FlutterError(code: "polarApiNil", message: "Polar API未初始化", details: nil)) } polarQueue.async { [weak self] in self?.autoConnectDisposable?.dispose() self?.autoConnectDisposable = api.startAutoConnectToDevice(-80, service: CBUUID(string: "180D"), polarDeviceType: nil) .subscribe(on: MainScheduler.instance) // 回调切回主线程 .subscribe { e in switch e { case .completed: result(nil) case .error(let err): result(FlutterError(code: "connect_failed", message: err.localizedDescription, details: nil)) @unknown default: break } } } }
- 确保
PolarBleApiObserver的回调在主线程执行(如果需要更新UI或通知Flutter):
func deviceConnected(_ identifier: PolarDeviceInfo) { DispatchQueue.main.async { NSLog("deviceConnected: \(identifier.deviceId)") // 若需通知Flutter,在此处调用Method Channel } }
内容来源于stack exchange
相关产品推荐
相关产品推荐

