如何让Expo/React Native计时器在后台/锁屏状态下运行并发送通知?
在Expo/React Native中实现后台运行的计时器并发送通知
完全可以实现,但不能依赖前端的setTimeout/setInterval——因为当应用进入后台或锁屏时,系统会挂起JS线程,这类定时器会暂停甚至失效。推荐两种方案,根据你的需求选择:
方案1:直接使用系统定时通知(推荐,轻量且可靠)
如果你的核心需求只是「计时结束后发送通知」,不需要在后台实时更新计时状态,直接用Expo的通知API设置定时触发即可,系统会负责在指定时间触发通知,完全不受应用状态影响。
实现步骤:
- 安装依赖:
npx expo install expo-notifications - 请求通知权限:
import * as Notifications from 'expo-notifications'; async function requestNotificationPermissions() { const { status } = await Notifications.requestPermissionsAsync(); return status === 'granted'; } - 调度定时通知:
// 假设需要倒计时60秒后触发通知 async function scheduleTimerNotification(seconds) { const hasPermission = await requestNotificationPermissions(); if (!hasPermission) return; await Notifications.scheduleNotificationAsync({ content: { title: '计时器结束', body: '你的倒计时已经完成!', sound: true, }, trigger: { seconds: seconds, // 延迟X秒后触发 repeats: false, }, }); }
这种方案不需要后台运行JS代码,完全由系统调度,省电且稳定,是最推荐的方式。
方案2:后台持续运行计时器(适合需要实时更新状态的场景)
如果需要在后台持续运行计时器(比如实时更新剩余时间到本地存储、同步到服务器等),需要结合Expo的后台任务工具:
实现步骤:
- 安装依赖:
npx expo install expo-task-manager expo-background-timer expo-notifications - 注册后台任务:
import * as TaskManager from 'expo-task-manager'; import * as BackgroundTimer from 'expo-background-timer'; import * as Notifications from 'expo-notifications'; // 定义后台任务名称 const TIMER_TASK = 'background-timer-task'; // 注册任务 TaskManager.defineTask(TIMER_TASK, async ({ data, error }) => { if (error) { console.error('后台任务出错:', error); return; } const { totalSeconds } = data; let remainingSeconds = totalSeconds; // 使用BackgroundTimer的setInterval,后台不会被挂起 const intervalId = BackgroundTimer.setInterval(() => { remainingSeconds--; if (remainingSeconds <= 0) { BackgroundTimer.clearInterval(intervalId); // 发送通知 Notifications.scheduleNotificationAsync({ content: { title: '计时器结束', body: '倒计时完成!', }, trigger: null, // 立即触发 }); } // 可选:更新本地存储的剩余时间 // AsyncStorage.setItem('remainingTime', remainingSeconds.toString()); }, 1000); }); - 启动后台任务:
async function startBackgroundTimer(seconds) { const hasPermission = await requestNotificationPermissions(); if (!hasPermission) return; // 请求后台任务权限 const { status } = await TaskManager.requestPermissionsAsync(); if (status !== 'granted') { alert('需要开启后台任务权限才能运行'); return; } // 启动任务 await TaskManager.startTaskAsync(TIMER_TASK, { data: { totalSeconds: seconds }, }); }
注意事项:
- 后台任务的运行时间会受系统限制(iOS尤其严格),长时间运行可能被系统终止;
- 后台任务会增加电量消耗,非必要不推荐使用;
- iOS上需要在
app.json中配置后台模式:{ "expo": { "ios": { "infoPlist": { "UIBackgroundModes": ["fetch", "remote-notification"] } } } }
关键提醒
- 优先使用方案1,系统级通知更稳定且省电;
- 无论哪种方案,都必须申请对应的权限(通知权限、后台任务权限);
- iOS上后台任务的限制比Android更严格,测试时需要注意系统行为。
内容的提问来源于stack exchange,提问作者maxi hraschan
相关产品推荐
相关产品推荐

