## 变更内容总结 1. **iOS后台任务系统重构** - 修复后台任务无法自动运行的问题 2. **日志系统优化** - 改进日志记录机制,添加队列和批量写入 3. **文档新增** - 添加后台任务修复总结和测试指南文档 4. **应用启动优化** - 添加后台任务状态检查和恢复逻辑 5. **版本号更新** - Info.plist版本从1.0.23升级到1.0.24 ## 提交信息类型判断 - **主要类型**: `fix` - 这是一个重要的bug修复,解决了iOS后台任务无法自动运行的核心问题 - **作用域**: `ios-background` - 专注于iOS后台任务功能 - **影响**: 这个修复对iOS用户的后台功能至关重要 ## 提交信息 fix(ios-background): 修复iOS后台任务无法自动运行的问题 主要修复内容: - 修复BackgroundTaskBridge任务调度逻辑,改用BGAppRefreshTaskRequest - 添加任务完成后自动重新调度机制,确保任务持续执行 - 优化应用生命周期管理,移除重复的后台任务调度 - 在应用启动时添加后台任务状态检查和恢复功能 - 将默认任务间隔从30分钟优化为15分钟 次要改进: - 重构日志系统,添加内存队列和批量写入机制,提升性能 - 添加写入锁和重试机制,防止日志数据丢失 - 新增详细的修复总结文档和测试指南 技术细节: - 使用BGAppRefreshTaskRequest替代BGProcessingTaskRequest - 实现任务过期自动重新调度 - 添加任务执行状态监控和恢复逻辑 - 优化错误处理和日志输出 影响范围: iOS后台任务调度、通知推送、应用状态管理
7.8 KiB
7.8 KiB
iOS 后台任务修复总结
问题分析
经过仔细检查代码,发现iOS端后台任务无法自动运行的根本原因:
1. 缺少应用生命周期监听
- 问题:后台任务只在应用初始化时调度一次,没有在应用进入后台时主动调度
- 影响:应用进入后台后,系统不知道需要执行后台任务
2. 任务类型选择不当
- 问题:使用了
BGProcessingTaskRequest(用于长时间处理任务) - 应该使用:
BGAppRefreshTaskRequest(用于定期刷新数据) - 影响:系统可能不会按预期调度任务
3. 任务调度不连续
- 问题:任务完成后没有自动重新调度下一次任务
- 影响:任务只执行一次就停止了
4. 延迟时间设置不合理
- 问题:30分钟的延迟时间对于测试来说太长
- 影响:难以验证功能是否正常工作
修复方案
1. AppDelegate.swift 修改
添加应用生命周期监听
public override func applicationDidEnterBackground(_ application: UIApplication) {
super.applicationDidEnterBackground(application)
// 当应用进入后台时,调度后台任务
if #available(iOS 13.0, *) {
scheduleBackgroundTask()
}
}
添加后台任务调度方法
@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 修改
改进任务处理逻辑
@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 ...
}
添加自动重新调度方法
@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)")
}
}
改进任务调度方法
@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 修改
减少默认延迟时间
const DEFAULT_RESCHEDULE_INTERVAL_SECONDS = 60 * 15; // 从30分钟改为15分钟
改进初始化逻辑
async initialize(): Promise<void> {
// ... 现有代码 ...
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();
}
添加任务调度方法
private async scheduleNextTask(): Promise<void> {
if (!isIosBackgroundModuleAvailable) {
return;
}
try {
await NativeBackgroundModule.schedule({
delay: DEFAULT_RESCHEDULE_INTERVAL_SECONDS
});
log.info('[BackgroundTaskManagerV2] 已调度下一次后台任务');
} catch (error) {
log.error('[BackgroundTaskManagerV2] 调度后台任务失败', error);
}
}
工作原理
完整的后台任务流程
-
应用启动
AppDelegate在didFinishLaunchingWithOptions中注册后台任务处理器BackgroundTaskManagerV2初始化并配置后台任务- 立即调度第一次后台任务
-
应用进入后台
AppDelegate.applicationDidEnterBackground被调用- 调用
scheduleBackgroundTask()调度后台任务 - 设置15分钟后开始执行
-
系统执行后台任务
- 系统在合适的时机(15分钟后或更晚)唤醒应用
- 调用
AppDelegate中注册的任务处理器 - 任务处理器通过 NotificationCenter 通知
BackgroundTaskBridge
-
任务执行
BackgroundTaskBridge发送事件到 JavaScriptBackgroundTaskManagerV2接收事件并执行后台任务- 执行喝水提醒、挑战鼓励、断食通知等任务
-
任务完成
- JavaScript 调用
complete()方法 BackgroundTaskBridge标记任务完成- 自动重新调度下一次任务
- JavaScript 调用
-
持续循环
- 每次任务完成后都会重新调度
- 确保后台任务持续执行
关键改进点
1. 使用 BGAppRefreshTaskRequest
- 更适合定期刷新数据的场景
- 系统调度更可靠
- 执行频率更高
2. 应用生命周期集成
- 在应用进入后台时主动调度
- 确保任务不会被遗漏
3. 自动重新调度
- 任务完成后自动调度下一次
- 形成持续的执行循环
4. 更短的延迟时间
- 15分钟的延迟更适合测试
- 提高任务执行频率
5. 更好的错误处理
- 添加详细的日志输出
- 处理各种异常情况
- 确保任务不会中断
测试建议
真机测试(推荐)
- 构建并安装到真机
- 启用后台应用刷新
- 将应用切换到后台
- 等待15-30分钟
- 检查是否收到通知
Xcode 模拟测试
- 在 Xcode 中运行应用
- 选择 Debug > Simulate Background Fetch
- 观察日志输出
- 验证任务执行
注意事项
- 模拟器支持有限,建议在真机上测试
- 系统调度时间不确定,需要耐心等待
- 低电量模式会限制后台任务
- 确保后台应用刷新已启用
预期效果
修复后,iOS 后台任务应该能够:
- ✅ 在应用进入后台时自动调度
- ✅ 每15-30分钟执行一次(取决于系统调度)
- ✅ 执行喝水提醒、挑战鼓励、断食通知等任务
- ✅ 任务完成后自动重新调度
- ✅ 持续运行,不会中断
相关文件
ios/OutLive/AppDelegate.swift- 应用生命周期和任务注册ios/OutLive/BackgroundTaskBridge.swift- 原生后台任务桥接services/backgroundTaskManagerV2.ts- JavaScript 后台任务管理器docs/BACKGROUND_TASK_TESTING.md- 详细测试指南