Files
frontend/src/services/backgroundService.ts
lafay 6f84e17772 feat(service): add foreground service for background message sync
Implement Android foreground service to keep app alive in background for real-time message sync:
- Add withForegroundService expo config plugin for notification channel setup
- Add BackgroundSyncManager with REALTIME, BATTERY_SAVER, and DISABLED modes
- Add ForegroundServiceModule for native Android foreground service binding
- Refactor backgroundService to integrate foreground service and expo-background-task

Also refactor auth and profile screens to use dynamic theme colors:
- Replace hardcoded THEME_COLORS with colors.primary.main, colors.background.paper, etc.
- Add useResolvedColorScheme for dynamic StatusBar styling
- Update NotificationSettingsScreen with sync mode selection UI

BREAKING CHANGE: Switched from expo-background-fetch to expo-background-task, minimum background sync interval changed from 900s to 15min in config
2026-04-11 04:27:22 +08:00

239 lines
6.1 KiB
TypeScript
Raw 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.
/**
* 后台保活服务
*
* 整合前台服务和后台同步
* - REALTIME 模式:使用 Android 前台服务保活(通知栏常驻)
* - BATTERY_SAVER 模式:使用 expo-background-task 定期同步最小15分钟间隔
*
* 参考Element Android 的 GuardAndroidService + BackgroundSyncStarter
*/
import { Platform } from 'react-native';
import * as BackgroundTask from 'expo-background-task';
import * as TaskManager from 'expo-task-manager';
import {
backgroundSyncManager,
BackgroundSyncMode,
} from './BackgroundSyncManager';
import { messageService } from './messageService';
import { api } from './api';
// 后台任务名称
const BACKGROUND_SYNC_TASK = 'background-sync-task';
// 后台任务间隔(分钟)
const BACKGROUND_INTERVAL_MINUTES = 15;
// 服务状态
let isInitialized = false;
// 定义后台任务(使用 expo-background-task
TaskManager.defineTask(BACKGROUND_SYNC_TASK, async () => {
try {
// 检查是否已认证
const token = await api.getToken();
if (!token) {
return BackgroundTask.BackgroundTaskResult.Success;
}
// 执行同步
await syncMessages();
return BackgroundTask.BackgroundTaskResult.Success;
} catch (error) {
console.error('[BackgroundService] 后台任务执行失败:', error);
return BackgroundTask.BackgroundTaskResult.Failed;
}
});
/**
* 执行消息同步
*/
async function syncMessages(): Promise<void> {
try {
// 同步未读数
const unreadData = await messageService.getUnreadCount();
const totalUnread = unreadData?.total_unread_count ?? 0;
// 更新前台服务通知
await backgroundSyncManager.updateNotification(totalUnread);
console.log('[BackgroundService] 同步完成, 未读数:', totalUnread);
} catch (error) {
console.error('[BackgroundService] 同步失败:', error);
}
}
/**
* 注册后台任务expo-background-task
*/
async function registerBackgroundTask(): Promise<void> {
if (Platform.OS === 'web') {
return;
}
try {
// 检查后台任务状态
const status = await BackgroundTask.getStatusAsync();
if (status !== BackgroundTask.BackgroundTaskStatus.Available) {
console.warn('[BackgroundService] 后台任务不可用,状态:', status);
return;
}
// 检查是否已注册
const isRegistered = await TaskManager.isTaskRegisteredAsync(BACKGROUND_SYNC_TASK);
if (!isRegistered) {
await BackgroundTask.registerTaskAsync(BACKGROUND_SYNC_TASK, {
minimumInterval: BACKGROUND_INTERVAL_MINUTES, // 以分钟为单位
});
console.log('[BackgroundService] 后台任务已注册');
}
} catch (error) {
console.error('[BackgroundService] 注册后台任务失败:', error);
}
}
/**
* 取消后台任务
*/
async function unregisterBackgroundTask(): Promise<void> {
if (Platform.OS === 'web') {
return;
}
try {
const isRegistered = await TaskManager.isTaskRegisteredAsync(BACKGROUND_SYNC_TASK);
if (isRegistered) {
await BackgroundTask.unregisterTaskAsync(BACKGROUND_SYNC_TASK);
console.log('[BackgroundService] 后台任务已取消');
}
} catch (error) {
console.error('[BackgroundService] 取消后台任务失败:', error);
}
}
/**
* 初始化后台保活服务
*/
export async function initBackgroundService(): Promise<boolean> {
if (isInitialized) {
return true;
}
if (Platform.OS === 'web') {
isInitialized = true;
return true;
}
try {
// 设置同步回调
backgroundSyncManager.setSyncMessagesCallback(syncMessages);
// 初始化后台同步管理器
await backgroundSyncManager.initialize();
// 注册 expo-background-task 任务(用于 BATTERY_SAVER 模式)
await registerBackgroundTask();
isInitialized = true;
console.log('[BackgroundService] 初始化完成');
return true;
} catch (error) {
console.error('[BackgroundService] 初始化失败:', error);
return false;
}
}
/**
* 停止后台保活服务
*/
export async function stopBackgroundService(): Promise<void> {
try {
await unregisterBackgroundTask();
await backgroundSyncManager.cleanup();
isInitialized = false;
console.log('[BackgroundService] 服务已停止');
} catch (error) {
console.error('[BackgroundService] 停止服务失败:', error);
}
}
/**
* 检查后台服务状态
*/
export async function checkBackgroundStatus(): Promise<{
isAvailable: boolean;
isInitialized: boolean;
registeredTasks: string[];
mode: BackgroundSyncMode;
}> {
if (Platform.OS === 'web') {
return {
isAvailable: false,
isInitialized,
registeredTasks: [],
mode: backgroundSyncManager.getMode(),
};
}
const status = await BackgroundTask.getStatusAsync();
const registeredTasks = await TaskManager.getRegisteredTasksAsync();
return {
isAvailable: status === BackgroundTask.BackgroundTaskStatus.Available,
isInitialized,
registeredTasks: registeredTasks.map((task) => task.taskName),
mode: backgroundSyncManager.getMode(),
};
}
/**
* 设置后台同步模式
*/
export async function setBackgroundSyncMode(mode: BackgroundSyncMode): Promise<void> {
await backgroundSyncManager.setMode(mode);
}
/**
* 获取当前后台同步模式
*/
export function getBackgroundSyncMode(): BackgroundSyncMode {
return backgroundSyncManager.getMode();
}
/**
* 手动触发同步
*/
export async function syncNow(): Promise<void> {
await syncMessages();
}
// 导出震动相关功能(保持向后兼容)
export {
triggerVibration,
vibrateOnMessage,
setVibrationConfig,
getVibrationConfig,
setVibrationEnabled,
} from './messageVibrationService';
// 重新导出类型
export { BackgroundSyncMode };
// 后台服务实例
export const backgroundService = {
init: initBackgroundService,
stop: stopBackgroundService,
setMode: setBackgroundSyncMode,
getMode: getBackgroundSyncMode,
syncNow,
checkStatus: checkBackgroundStatus,
// 震动相关
vibrate: async () => {
const { triggerVibration } = await import('./messageVibrationService');
triggerVibration();
},
};
export default backgroundService;