Files
digital-pilates/docs/BACKGROUND_TASK_FIX_SUMMARY.md
richarjiang d74046498d # 分析方案
## 变更内容总结
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后台任务调度、通知推送、应用状态管理
2025-11-04 19:14:53 +08:00

7.8 KiB
Raw Permalink Blame History

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);
    }
}

工作原理

完整的后台任务流程

  1. 应用启动

    • AppDelegatedidFinishLaunchingWithOptions 中注册后台任务处理器
    • 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 - 详细测试指南