feat(background-task): 完善iOS后台任务系统并优化断食通知和UI体验

- 修复iOS后台任务注册时机问题,确保任务能正常触发
- 添加后台任务调试辅助工具和完整测试指南
- 优化断食通知系统,增加防抖机制避免频繁重调度
- 改进断食自动续订逻辑,使用固定时间而非相对时间计算
- 优化统计页面布局,添加身体指标section标题
- 增强饮水详情页面视觉效果,改进卡片样式和配色
- 添加用户反馈入口到个人设置页面
- 完善锻炼摘要卡片条件渲染逻辑
- 增强日志记录和错误处理机制

这些改进显著提升了应用的稳定性、性能和用户体验,特别是在iOS后台任务执行和断食功能方面。
This commit is contained in:
richarjiang
2025-11-05 11:23:33 +08:00
parent d74046498d
commit ea22901553
12 changed files with 1060 additions and 171 deletions

View File

@@ -339,37 +339,52 @@ export class BackgroundTaskManagerV2 {
return;
}
logger.info('[BackgroundTaskManagerV2] ====== 开始初始化后台任务管理器 ======');
if (!isIosBackgroundModuleAvailable) {
logger.warn('[BackgroundTaskManagerV2] iOS 原生后台模块不可用,跳过初始化');
logger.warn('[BackgroundTaskManagerV2] Platform:', Platform.OS);
this.isInitialized = false;
return;
}
logger.info('[BackgroundTaskManagerV2] 原生模块可用,开始注册事件监听器');
const emitter = new NativeEventEmitter(NativeBackgroundModule);
this.eventSubscription = emitter.addListener(BACKGROUND_EVENT, (payload) => {
logger.info('[BackgroundTaskManagerV2] 收到后台任务事件', payload);
logger.info('[BackgroundTaskManagerV2] 收到后台任务执行事件', payload);
this.handleBackgroundExecution();
});
this.expirationSubscription = emitter.addListener(EXPIRATION_EVENT, (payload) => {
logger.warn('[BackgroundTaskManagerV2] 后台任务在完成前即将过期', payload);
logger.warn('[BackgroundTaskManagerV2] ⚠️ 后台任务即将过期', payload);
// 处理任务过期情况,确保重新调度
this.handleTaskExpiration();
});
logger.info('[BackgroundTaskManagerV2] 事件监听器注册完成');
try {
// 检查后台刷新状态
logger.info('[BackgroundTaskManagerV2] 检查后台刷新权限状态...');
const status = await this.getStatus();
logger.info('[BackgroundTaskManagerV2] 后台刷新状态:', status);
if (status === 'denied' || status === 'restricted') {
logger.warn('[BackgroundTaskManagerV2] 后台刷新被限制或拒绝,后台任务可能无法正常工作');
// 不抛出错误,但标记为未完全初始化
if (status === 'denied') {
logger.error('[BackgroundTaskManagerV2] 后台刷新被拒绝!');
logger.error('[BackgroundTaskManagerV2] 请在 设置 > Out Live > 后台App刷新 中启用');
this.isInitialized = false;
return;
}
if (status === 'restricted') {
logger.warn('[BackgroundTaskManagerV2] ⚠️ 后台刷新被限制(可能是家长控制)');
this.isInitialized = false;
return;
}
logger.info('[BackgroundTaskManagerV2] 配置后台任务...');
await NativeBackgroundModule.configure({
identifier: BACKGROUND_TASK_IDENTIFIER,
taskType: 'refresh',
@@ -377,16 +392,24 @@ export class BackgroundTaskManagerV2 {
requiresExternalPower: false,
defaultDelay: DEFAULT_RESCHEDULE_INTERVAL_SECONDS,
});
this.isInitialized = true;
logger.info('[BackgroundTaskManagerV2] 已初始化并注册 iOS 后台任务');
logger.info('[BackgroundTaskManagerV2] 后台任务配置成功');
// 立即调度一次后台任务
logger.info('[BackgroundTaskManagerV2] 调度首次后台任务...');
await this.scheduleNextTask();
// 检查待处理的任务请求
const pendingRequests = await this.getPendingRequests();
logger.info('[BackgroundTaskManagerV2] 当前待处理的任务请求数量:', pendingRequests.length);
if (pendingRequests.length > 0) {
logger.info('[BackgroundTaskManagerV2] 待处理任务详情:', JSON.stringify(pendingRequests, null, 2));
}
logger.info('[BackgroundTaskManagerV2] ====== 初始化完成 ======');
} catch (error: any) {
// BGTaskSchedulerErrorDomain 错误码 1 表示后台任务功能不可用
// 这在模拟器上是正常的,因为模拟器不完全支持后台任务
@@ -395,7 +418,9 @@ export class BackgroundTaskManagerV2 {
(errorMessage.includes('错误1') || errorMessage.includes('code 1'));
if (isBGTaskUnavailable) {
logger.warn('[BackgroundTaskManagerV2] 后台任务功能在当前环境不可用(模拟器限制),将在真机上正常工作');
logger.warn('[BackgroundTaskManagerV2] ⚠️ 后台任务功能在当前环境不可用');
logger.warn('[BackgroundTaskManagerV2] 这是模拟器的正常限制,在真机上会正常工作');
logger.warn('[BackgroundTaskManagerV2] 建议:在真机上测试后台任务功能');
this.removeListeners();
this.isInitialized = false;
// 不抛出错误,因为这是预期行为
@@ -403,12 +428,15 @@ export class BackgroundTaskManagerV2 {
}
// 其他错误情况,尝试恢复
logger.error('[BackgroundTaskManagerV2] 初始化失败,尝试恢复', error);
logger.error('[BackgroundTaskManagerV2] 初始化失败', error);
logger.error('[BackgroundTaskManagerV2] 错误详情:', errorMessage);
try {
// 尝试重新初始化一次
logger.info('[BackgroundTaskManagerV2] 尝试恢复...');
await this.attemptRecovery();
logger.info('[BackgroundTaskManagerV2] ✅ 恢复成功');
} catch (recoveryError) {
logger.error('[BackgroundTaskManagerV2] 恢复失败,放弃初始化', recoveryError);
logger.error('[BackgroundTaskManagerV2] 恢复失败,放弃初始化', recoveryError);
this.removeListeners();
throw error;
}
@@ -569,14 +597,41 @@ export class BackgroundTaskManagerV2 {
async triggerTaskForTesting(): Promise<void> {
if (!isIosBackgroundModuleAvailable) {
logger.info('[BackgroundTaskManagerV2] 原生模块不可用,直接执行后台任务逻辑');
await executeBackgroundTasks();
return;
}
try {
logger.info('[BackgroundTaskManagerV2] 尝试模拟触发后台任务...');
await NativeBackgroundModule.simulateLaunch();
} catch (error) {
logger.error('[BackgroundTaskManagerV2] 模拟后台任务触发失败', error);
logger.info('[BackgroundTaskManagerV2] ✅ 模拟触发成功');
} catch (error: any) {
const errorMessage = error?.message || String(error);
const errorCode = error?.code || '';
// 检查是否是模拟器不支持的错误
if (errorCode === 'SIMULATOR_NOT_SUPPORTED') {
logger.warn('[BackgroundTaskManagerV2] ⚠️ 模拟器不支持后台任务');
logger.warn('[BackgroundTaskManagerV2] 这是正常的限制,请在真机上测试');
logger.info('[BackgroundTaskManagerV2] 作为替代,直接执行后台任务逻辑...');
// 在模拟器上直接执行后台任务逻辑作为测试
await executeBackgroundTasks();
return;
}
// 检查是否是监听器未注册的错误
if (errorCode === 'NO_LISTENERS') {
logger.warn('[BackgroundTaskManagerV2] ⚠️ JS 监听器未注册');
logger.warn('[BackgroundTaskManagerV2] 可能是应用还未完全初始化');
logger.info('[BackgroundTaskManagerV2] 尝试直接执行后台任务逻辑...');
await executeBackgroundTasks();
return;
}
logger.error('[BackgroundTaskManagerV2] ❌ 模拟后台任务触发失败', error);
logger.error('[BackgroundTaskManagerV2] 错误代码:', errorCode);
logger.error('[BackgroundTaskManagerV2] 错误信息:', errorMessage);
throw error;
}
}