如何在Expo版React Native中实现Background Fetch解决离线推送问题
嘿,我刚好有过用Expo实现这个需求的经验,给你梳理下具体的步骤和注意事项,帮你搞定应用完全关闭时的推送通知问题:
核心思路:用Expo Background Fetch + 本地通知补全离线推送
因为应用完全关闭时,没法直接接收远程推送(FCM/APNs找不到活跃的应用进程),所以我们需要靠后台定时任务主动拉取新通知,然后触发本地通知来提醒用户。
第一步:配置项目权限与基础依赖
首先得确保项目有后台任务和通知的权限,同时安装必要的Expo包:
- 安装依赖:
npx expo install expo-background-fetch expo-notifications - 修改
app.json配置权限:- iOS端:需要开启后台fetch权限,在
expo.ios.infoPlist里添加:"UIBackgroundModes": ["fetch", "remote-notification"] - Android端:添加必要的权限和后台配置,在
expo.android里补充:"permissions": [ "android.permission.FOREGROUND_SERVICE", "android.permission.RECEIVE_BOOT_COMPLETED", "android.permission.WAKE_LOCK" ], "backgroundPermissions": ["android.permission.ACCESS_BACKGROUND_LOCATION"], "useNextNotificationsApi": true
- iOS端:需要开启后台fetch权限,在
第二步:注册后台Fetch任务
在App启动时注册后台任务,比如在App.js的useEffect里:
import { BackgroundFetch } from 'expo-background-fetch'; import { registerTaskAsync } from 'expo-task-manager'; // 先定义任务名称 const BACKGROUND_FETCH_TASK = 'fetch-new-notifications'; useEffect(() => { const setupBackgroundFetch = async () => { // 注册任务处理函数 await registerTaskAsync(BACKGROUND_FETCH_TASK, { minimumInterval: 60, // 注意:iOS系统强制最小15分钟(900秒),填更小的值会被系统自动调整 stopOnTerminate: false, // Android:应用关闭后是否继续运行任务 startOnBoot: true, // Android:开机后自动启动任务 }); // 申请后台fetch权限 const status = await BackgroundFetch.getStatusAsync(); if (status !== BackgroundFetch.Status.Available) { await BackgroundFetch.requestPermissionsAsync(); } }; setupBackgroundFetch(); }, []);
划重点:iOS的
minimumInterval最小是15分钟,系统会忽略更小的设置,没法做到精确1分钟的间隔;Android可以设置更短,但要注意系统的电池优化可能会限制任务触发频率。
第三步:编写后台任务处理逻辑
在项目根目录创建一个backgroundTasks.js文件,编写拉取通知和触发本地通知的逻辑:
import { BackgroundFetch } from 'expo-background-fetch'; import * as Notifications from 'expo-notifications'; export async function fetchNewNotificationsTask() { try { // 1. 调用你的API检查特定用户的新通知 const response = await fetch('https://your-api-url/check-notifications', { method: 'POST', headers: { 'Content-Type': 'application/json', // 带上用户身份凭证,比如token 'Authorization': 'Bearer ' + userToken, }, body: JSON.stringify({ userId: 'specific-user-id' }), }); const newNotifications = await response.json(); // 2. 如果有新通知,触发本地通知 if (newNotifications.length > 0) { for (const notification of newNotifications) { await Notifications.scheduleNotificationAsync({ content: { title: notification.title, body: notification.content, data: notification.data, // 携带自定义数据,方便点击通知跳转 }, trigger: null, // 立即触发通知 }); } } // 告诉系统任务成功,有新数据 return BackgroundFetch.Result.NewData; } catch (error) { console.error('Background fetch failed:', error); // 任务失败,告知系统 return BackgroundFetch.Result.Failed; } }
然后在App.js里关联这个任务,确保Expo能找到它:
import { fetchNewNotificationsTask } from './backgroundTasks'; // 注册任务时绑定处理函数 registerTaskAsync(BACKGROUND_FETCH_TASK, { task: fetchNewNotificationsTask });
第四步:关键注意事项
- iOS的限制:
- 应用被用户从多任务划掉后,后台任务会停止,直到用户再次打开应用才会恢复。
- 后台fetch的触发时间由系统调度,只有当设备充电、联网且空闲时才会频繁触发,没法保证精确间隔。
- 如果需要更实时的通知,可结合APNs的静默通知,但静默通知也有严格限制(不能显示内容,只能触发少量逻辑)。
- Android的优化规避:
- 部分国产手机(小米、华为等)的电池优化会强制停止后台任务,需要引导用户手动关闭应用的电池优化。
- 可以申请
REQUEST_IGNORE_BATTERY_OPTIMIZATIONS权限,让用户授权忽略电池优化。
- 后台任务限制:
- 后台任务的执行时间有限(iOS约30秒,Android约1分钟),所以逻辑要尽量简洁,避免耗时操作。
- 不能在后台任务中更新UI或调用Alert等交互组件,只能做网络请求和触发本地通知。
第五步:测试方法
- iOS:用Xcode打开项目的workspace,选择
Debug -> Simulate Background Fetch,就能触发后台任务测试。 - Android:用adb命令触发任务:
(job-id可以通过adb shell cmd jobscheduler run -f your-expo-package-name job-idBackgroundFetch.getTaskAsync()获取,或者查看Expo运行日志)
内容的提问来源于stack exchange,提问作者Ilya K




