# iOS 后台任务修复总结 ## 问题分析 经过仔细检查代码,发现iOS端后台任务无法自动运行的根本原因: ### 1. 缺少应用生命周期监听 - **问题**:后台任务只在应用初始化时调度一次,没有在应用进入后台时主动调度 - **影响**:应用进入后台后,系统不知道需要执行后台任务 ### 2. 任务类型选择不当 - **问题**:使用了 `BGProcessingTaskRequest`(用于长时间处理任务) - **应该使用**:`BGAppRefreshTaskRequest`(用于定期刷新数据) - **影响**:系统可能不会按预期调度任务 ### 3. 任务调度不连续 - **问题**:任务完成后没有自动重新调度下一次任务 - **影响**:任务只执行一次就停止了 ### 4. 延迟时间设置不合理 - **问题**:30分钟的延迟时间对于测试来说太长 - **影响**:难以验证功能是否正常工作 ## 修复方案 ### 1. AppDelegate.swift 修改 #### 添加应用生命周期监听 ```swift public override func applicationDidEnterBackground(_ application: UIApplication) { super.applicationDidEnterBackground(application) // 当应用进入后台时,调度后台任务 if #available(iOS 13.0, *) { scheduleBackgroundTask() } } ``` #### 添加后台任务调度方法 ```swift @available(iOS 13.0, *) private func scheduleBackgroundTask() { let identifier = "com.anonymous.digitalpilates.task" // 取消之前的任务请求 BGTaskScheduler.shared.cancel(taskRequestWithIdentifier: identifier) let request = BGAppRefreshTaskRequest(identifier: identifier) // 设置最早开始时间为15分钟后 request.earliestBeginDate = Date(timeIntervalSinceNow: 15 * 60) do { try BGTaskScheduler.shared.submit(request) NSLog("[AppDelegate] 后台任务已调度,将在15分钟后执行") } catch { NSLog("[AppDelegate] 调度后台任务失败: \(error.localizedDescription)") } } ``` ### 2. BackgroundTaskBridge.swift 修改 #### 改进任务处理逻辑 ```swift @available(iOS 13.0, *) private func handle(task: BGTask) { // ... 现有代码 ... guard self.hasListeners else { NSLog("[BackgroundTaskBridge] 没有JS监听器,直接完成任务") task.setTaskCompleted(success: false) self.currentTask = nil // 重新调度下一次任务 self.rescheduleTask() return } // ... 发送事件到JS ... } ``` #### 添加自动重新调度方法 ```swift @available(iOS 13.0, *) private func rescheduleTask() { guard let identifier = self.identifier else { return } let request = BGAppRefreshTaskRequest(identifier: identifier) request.earliestBeginDate = Date(timeIntervalSinceNow: self.defaultDelay) do { try BGTaskScheduler.shared.submit(request) NSLog("[BackgroundTaskBridge] 已重新调度后台任务,延迟: \(self.defaultDelay)秒") } catch { NSLog("[BackgroundTaskBridge] 重新调度后台任务失败: \(error.localizedDescription)") } } ``` #### 改进任务调度方法 ```swift @available(iOS 13.0, *) private func scheduleTask(after delay: TimeInterval) throws { guard let identifier else { throw NSError(...) } // 取消之前的任务请求 BGTaskScheduler.shared.cancel(taskRequestWithIdentifier: identifier) // 使用 BGAppRefreshTaskRequest let request = BGAppRefreshTaskRequest(identifier: identifier) request.earliestBeginDate = Date(timeIntervalSinceNow: delay) do { try BGTaskScheduler.shared.submit(request) NSLog("[BackgroundTaskBridge] 后台任务已调度,标识符: \(identifier),延迟: \(delay)秒") } catch { NSLog("[BackgroundTaskBridge] 调度后台任务失败: \(error.localizedDescription)") throw error } } ``` ### 3. backgroundTaskManagerV2.ts 修改 #### 减少默认延迟时间 ```typescript const DEFAULT_RESCHEDULE_INTERVAL_SECONDS = 60 * 15; // 从30分钟改为15分钟 ``` #### 改进初始化逻辑 ```typescript async initialize(): Promise { // ... 现有代码 ... await NativeBackgroundModule.configure({ identifier: BACKGROUND_TASK_IDENTIFIER, taskType: 'refresh', // 从 'processing' 改为 'refresh' requiresNetworkConnectivity: false, requiresExternalPower: false, defaultDelay: DEFAULT_RESCHEDULE_INTERVAL_SECONDS, }); this.isInitialized = true; log.info('[BackgroundTaskManagerV2] 已初始化并注册 iOS 后台任务'); // 立即调度一次后台任务 await this.scheduleNextTask(); } ``` #### 添加任务调度方法 ```typescript private async scheduleNextTask(): Promise { if (!isIosBackgroundModuleAvailable) { return; } try { await NativeBackgroundModule.schedule({ delay: DEFAULT_RESCHEDULE_INTERVAL_SECONDS }); log.info('[BackgroundTaskManagerV2] 已调度下一次后台任务'); } catch (error) { log.error('[BackgroundTaskManagerV2] 调度后台任务失败', error); } } ``` ## 工作原理 ### 完整的后台任务流程 1. **应用启动** - `AppDelegate` 在 `didFinishLaunchingWithOptions` 中注册后台任务处理器 - `BackgroundTaskManagerV2` 初始化并配置后台任务 - 立即调度第一次后台任务 2. **应用进入后台** - `AppDelegate.applicationDidEnterBackground` 被调用 - 调用 `scheduleBackgroundTask()` 调度后台任务 - 设置15分钟后开始执行 3. **系统执行后台任务** - 系统在合适的时机(15分钟后或更晚)唤醒应用 - 调用 `AppDelegate` 中注册的任务处理器 - 任务处理器通过 NotificationCenter 通知 `BackgroundTaskBridge` 4. **任务执行** - `BackgroundTaskBridge` 发送事件到 JavaScript - `BackgroundTaskManagerV2` 接收事件并执行后台任务 - 执行喝水提醒、挑战鼓励、断食通知等任务 5. **任务完成** - JavaScript 调用 `complete()` 方法 - `BackgroundTaskBridge` 标记任务完成 - 自动重新调度下一次任务 6. **持续循环** - 每次任务完成后都会重新调度 - 确保后台任务持续执行 ## 关键改进点 ### 1. 使用 BGAppRefreshTaskRequest - 更适合定期刷新数据的场景 - 系统调度更可靠 - 执行频率更高 ### 2. 应用生命周期集成 - 在应用进入后台时主动调度 - 确保任务不会被遗漏 ### 3. 自动重新调度 - 任务完成后自动调度下一次 - 形成持续的执行循环 ### 4. 更短的延迟时间 - 15分钟的延迟更适合测试 - 提高任务执行频率 ### 5. 更好的错误处理 - 添加详细的日志输出 - 处理各种异常情况 - 确保任务不会中断 ## 测试建议 ### 真机测试(推荐) 1. 构建并安装到真机 2. 启用后台应用刷新 3. 将应用切换到后台 4. 等待15-30分钟 5. 检查是否收到通知 ### Xcode 模拟测试 1. 在 Xcode 中运行应用 2. 选择 Debug > Simulate Background Fetch 3. 观察日志输出 4. 验证任务执行 ### 注意事项 - 模拟器支持有限,建议在真机上测试 - 系统调度时间不确定,需要耐心等待 - 低电量模式会限制后台任务 - 确保后台应用刷新已启用 ## 预期效果 修复后,iOS 后台任务应该能够: 1. ✅ 在应用进入后台时自动调度 2. ✅ 每15-30分钟执行一次(取决于系统调度) 3. ✅ 执行喝水提醒、挑战鼓励、断食通知等任务 4. ✅ 任务完成后自动重新调度 5. ✅ 持续运行,不会中断 ## 相关文件 - `ios/OutLive/AppDelegate.swift` - 应用生命周期和任务注册 - `ios/OutLive/BackgroundTaskBridge.swift` - 原生后台任务桥接 - `services/backgroundTaskManagerV2.ts` - JavaScript 后台任务管理器 - `docs/BACKGROUND_TASK_TESTING.md` - 详细测试指南