Highlight
watchOS 8 允许应用在 Background App Refresh 期间连接蓝牙设备,这让 complications 可以持续从蓝牙配件获取数据并更新,无需用户主动打开应用。
核心内容
以前的问题:蓝牙只能在应用前台连接
(00:31)
在 watchOS 7 及之前,应用只能在两种状态下连接蓝牙设备:前台运行时,或后台会话期间。这意味着如果用户想看 complication 上显示蓝牙设备的最新数据,必须先打开应用建立连接。
这对很多场景都不够友好。比如一个温度传感器,用户希望抬腕就能看到当前温度,而不是每次都要打开应用。
watchOS 8 的解决方案:后台刷新期间连接蓝牙
(00:57)
watchOS 8 允许应用在 Background App Refresh 期间连接蓝牙配件。这带来了两个核心能力:
- 更新 complications:从蓝牙设备获取数据,直接更新表盘上的 complication
- 发送本地通知:比如蓝牙设备电量低时提醒用户
要使用这个功能,只需要在 Info.plist 中添加 bluetooth-central 到 UIBackgroundModes。
连接的生命周期
(02:04)
蓝牙连接的生命周期分几个阶段:
首次配对(前台):
- 用户打开应用
- 应用扫描并发现蓝牙设备
- 建立初始连接,可能需要配对
- 用户退出应用,蓝牙连接断开
后台刷新期间(watchOS 8 新增):
- 系统触发 Background App Refresh
- 应用发起对已配对设备的连接请求
- 设备发送 advertisement,Apple Watch 建立连接
- 应用获取数据,处理完成后主动断开连接
(02:36)
配件设计的最佳实践
(02:55)
Background App Refresh 可能在任何时间发生,所以配件应该在有重要更新时尽可能频繁地广播。如果配件为了省电很少广播,可能在后台刷新期间收不到 advertisement,导致连接失败。
一个策略是在配件上缓冲传感器数据。当缓冲区快满时,提高广播频率以增加与 Apple Watch 重新连接的机会。
(03:37)
在理想的射频条件下,建议至少每两秒广播一次。如果在更具挑战性的射频环境中使用,应该更频繁地广播。
三种用户流程
(04:02)
Session 展示了三种可能的连接流程:
流程一:正常连接和断开
- 应用在后台刷新期间发起连接
- 收到 advertisement,建立连接
- 获取数据,调用
cancelPeripheralConnection主动断开 - 这是最理想的流程
流程二:配件不在附近或未广播
- 应用发起连接,但没有收到 advertisement
- expiration handler 被调用
- 应用取消连接请求,准备下一次后台刷新
- 建议主动在 expiration handler 之前断开以节省电量
流程三:未在后台刷新结束前断开
- 应用建立连接,但在后台刷新结束前没来得及断开
- Core Bluetooth 会在后台刷新结束时自动断开连接
- 下一次后台刷新时,应用收到
didDisconnectPeripheral回调 - 应用可以重新发起连接或等待下一次刷新
(05:28)
详细内容
前台发现设备
(07:35)
当用户在前台使用应用时,发现设备并连接:
func centralManager(_ central: CBCentralManager,
didDiscover peripheral: CBPeripheral,
advertisementData: [String: Any],
rssi RSSI: NSNumber) {
// 添加到已发现设备列表,然后连接
central.connect(peripheral, options: nil)
}
关键点:
didDiscoverPeripheral在检测到 advertisement 时调用- 将发现的设备保存到数组中供后续使用
central.connect发起连接请求
后台刷新期间连接
(07:55)
在 Extension Delegate 的后台任务处理中发起蓝牙连接:
func handle(_ backgroundTasks: Set<WKRefreshBackgroundTask>) {
for task in backgroundTasks {
if let refreshTask = task as? WKApplicationRefreshBackgroundTask {
// 开始后台工作:连接蓝牙设备
central.connect(peripheral, options: nil)
refreshTask.expirationHandler = {
// 后台时间即将用完,取消连接
if let peripheral = self.bluetoothReceiver.connectedPeripheral {
self.central.cancelPeripheralConnection(peripheral)
}
refreshTask.setTaskCompletedWithSnapshot(false)
}
}
}
}
关键点:
- 在
WKApplicationRefreshBackgroundTask中发起蓝牙连接 expirationHandler是 watchOS 8 新增 API,在后台时间即将用完时调用- 在 expiration handler 中取消连接并标记任务完成
- 主动在 expiration handler 之前断开连接可以节省电量
断开连接的处理
(08:51)
当蓝牙设备断开时,系统会调用 delegate 方法:
func centralManager(_ central: CBCentralManager,
didDisconnectPeripheral peripheral: CBPeripheral,
error: Error?) {
connectedPeripheral = nil
delegate?.didCompleteDisconnection(from: peripheral)
}
关键点:
didDisconnectPeripheral在前后台都可能被调用- 清空
connectedPeripheral引用 - 通知 delegate 断开完成
在 Extension Delegate 中完成后台任务
(09:08)
func didCompleteDisconnection(from peripheral: CBPeripheral) {
if let refreshTask = currentRefreshTask {
refreshTask.setTaskCompletedWithSnapshot(false)
currentRefreshTask = nil
}
}
关键点:
- 确认当前处于 Background App Refresh 状态
- 标记后台任务完成
- 这会终止 Background App Refresh,让应用回到挂起状态
- 将
currentRefreshTask设为 nil 避免重复完成
Info.plist 配置
要使用后台蓝牙连接,需要在 WatchKit Extension 的 Info.plist 中添加:
<key>UIBackgroundModes</key>
<array>
<string>bluetooth-central</string>
</array>
(01:35)
这也可以显示为 “Required background modes” 和 “App communicates using CoreBluetooth”。
平台支持配置对比
(01:50)
不同平台上蓝牙低功耗的支持配置有所不同。在 watchOS 上,当前支持的配置包括前台连接和后台刷新期间的连接。后台扫描新设备只在 iOS 上支持,watchOS 不支持在后台扫描新设备。
首次设备设置的要求
(09:26)
首次设置蓝牙设备时,应用必须在前台运行。用户需要主动使用应用,此时可以扫描新设备并建立初始连接。一旦设备被发现并配对,后续就可以在后台刷新期间重新连接。
应用退出前台后不能扫描新设备,只能请求连接已发现的设备。连接后获取数据,然后应该主动断开以保存电量。
核心启发
-
做一个蓝牙温度计 complication:连接蓝牙温度传感器,在表盘 complication 上实时显示当前温度。利用后台刷新每小时最多四次更新数据。入口 API 是
CBCentralManager+WKApplicationRefreshBackgroundTask。 -
做一个运动 GPS 追踪器配套应用:连接蓝牙 GPS 追踪器,在 complication 上显示配速和距离。运动结束后自动断开以省电。入口 API 是
CBPeripheral的didUpdateValueFor+HKWorkoutSession。 -
做一个医疗设备数据监测应用:连接蓝牙血氧仪或血压计,在后台刷新期间读取数据并记录到 HealthKit。数据异常时发送本地通知提醒。入口 API 是
HKHealthStore.save+UNUserNotificationCenter。 -
做一个蓝牙设备电量监控工具:在后台连接时读取蓝牙设备的电池电量,电量低于阈值时在 complication 上显示警告图标。入口 API 是
CBCharacteristic的电池服务 UUID0x180F。 -
做一个智能家居手表遥控器:连接蓝牙门锁或灯光控制器,在 complication 上显示设备状态,点击快速控制。入口 API 是
CBPeripheral.writeValue+ 自定义 GATT 服务。
关联 Session
- What’s new in watchOS 8 — watchOS 8 新特性概览,包括蓝牙后台连接的背景介绍
- Build a workout app for Apple Watch — 用 SwiftUI 和 HealthKit 构建 workout app,可结合蓝牙传感器
- There and back again: Data transfer on Apple Watch — Apple Watch 数据通信策略,包括后台任务处理
评论
GitHub Issues · utterances