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

266 lines
7.8 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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<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();
}
```
#### 添加任务调度方法
```typescript
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. **应用启动**
- `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` - 详细测试指南