获取到Token但Expo-notifications在真机上无法正常工作
Expo Notifications 推送通知无法接收排查
问题描述
开发React Native Expo应用时,复用了之前可正常发送推送通知的代码,但当前构建版本中功能失效。使用npx expo start --dev-client在已注册到Expo项目的iPhone真机上运行应用,无论是通过Expo Notifications页面还是调用sendPushNotification()方法,请求均返回200状态码,但设备始终无法收到通知。已确认设备通知权限开启,且未处于静音状态。
依赖版本
package.json中相关依赖:
"expo-notifications": "^0.27.7"
代码实现
自定义Hook usePushNotifications
import React, { useState, useEffect, useRef } from "react"; import * as Device from "expo-device"; import * as Notifications from "expo-notifications"; import Constants from "expo-constants"; import { Platform } from "react-native"; export const usePushNotifications = () => { Notifications.setNotificationHandler({ handleNotification: async () => ({ shouldPlaySound: true, shouldShowAlert: true, shouldSetBadge: false, }), }); // 发送推送通知方法 async function sendPushNotification(expoPushToken) { const message = { to: expoPushToken, sound: "default", title: "Original Title", body: "And here is the body!", data: { someData: "goes here" }, }; await fetch("https://exp.host/--/api/v2/push/send", { method: "POST", headers: { Accept: "application/json", "Accept-encoding": "gzip, deflate", "Content-Type": "application/json", }, body: JSON.stringify(message), }); } const [expoPushToken, setExpoPushToken] = useState(""); const [notification, setNotification] = useState(false); const notificationListener = useRef(); const responseListener = useRef(); async function registerForPushNotificationsAsync() { let token; if (Platform.OS === "android") { await Notifications.setNotificationChannelAsync("default", { name: "default", importance: Notifications.AndroidImportance.MAX, vibrationPattern: [0, 250, 250, 250], lightColor: "#FF231F7C", }); } if (Device.isDevice) { const { status: existingStatus } = await Notifications.getPermissionsAsync(); let finalStatus = existingStatus; if (existingStatus !== "granted") { const { status } = await Notifications.requestPermissionsAsync(); finalStatus = status; } if (finalStatus !== "granted") { alert("Failed to get push token for push notification!"); return; } token = await Notifications.getExpoPushTokenAsync({ projectId: Constants.expoConfig?.extra?.eas.projectId, }); } else { alert("Must be using a physical device for Push Notifications"); } return token; } useEffect(() => { registerForPushNotificationsAsync().then((token) => setExpoPushToken(token) ); notificationListener.current = Notifications.addNotificationReceivedListener((notification) => { setNotification(notification); }); // 重复注册了相同的监听器,覆盖了之前的引用 notificationListener.current = Notifications.addNotificationReceivedListener((notification) => { setNotification(notification); }); responseListener.current = Notifications.addNotificationResponseReceivedListener((response) => { console.log(response); }); return () => { Notifications.removeNotificationSubscription( notificationListener.current ); Notifications.removeNotificationSubscription(responseListener.current); }; }, []); return { expoPushToken, notification, }; };
在App.js中使用
const { expoPushToken } = usePushNotifications(); console.log("Push Notifications token :", expoPushToken);
测试情况
调用推送接口后,控制台返回的响应:
Notification Response : {"_bodyBlob": {"_data": {"__collector": [Object], "blobId": "B44199F9-59EE-4CD2-9D2E-DC088353524F", "name": "send.json", "offset": 0, "size": 68, "type": "application/json"}}, "_bodyInit": {"_data": {"__collector": [Object], "blobId": "B44199F9-59EE-4CD2-9D2E-DC088353524F", "name": "send.json", "offset": 0, "size": 68, "type": "application/json"}}, "bodyUsed": false, "headers": {"map": {"alt-svc": "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000", "content-length": "68", "content-type": "application/json; charset=utf-8", "date": "Sun, 28 Apr 2024 05:19:26 GMT", "etag": "\"44-qrsRHa9ppS08dGDo3z285YOag10\"", "strict-transport-security": "max-age=31536000; includeSubDomains", "vary": "Accept-Encoding, Origin", "via": "1.1 google", "x-content-type-options": "nosniff", "x-frame-options": "SAMEORIGIN"}}, "ok": true, "status": 200, "statusText": "", "type": "default", "url": "https://exp.host/--/api/v2/push/send"}
状态码为200,但设备无任何通知提示。
已尝试操作
- 重新创建Expo应用并复制所有代码
- 删除
node_modules后重新执行yarn install
排查建议
- 检查Project ID有效性:在获取推送token时,确认
Constants.expoConfig?.extra?.eas.projectId能正确获取到Expo项目的ID,可通过console.log(Constants.expoConfig?.extra?.eas.projectId)验证,若为空则手动传入项目ID。 - 修复监听器重复注册问题:代码中
useEffect内重复注册了两次addNotificationReceivedListener,会覆盖之前的监听器引用,导致第一个监听器无法被正确移除,删除其中一次重复注册。 - 解析推送接口响应内容:当前仅检查了状态码200,需解析响应体查看是否存在错误信息,修改
sendPushNotification方法:const response = await fetch("https://exp.host/--/api/v2/push/send", { ... }); const data = await response.json(); console.log("Push response data:", data); - 验证iOS推送证书配置:登录Expo控制台,确认项目的iOS推送证书已正确配置且未过期,证书无效会导致推送无法送达。
- 重置设备推送权限:在iPhone设置→应用→你的应用→通知中,关闭通知权限后重新开启,重启应用并重新获取推送token。
- 检查SDK版本兼容性:确认
expo-notifications@0.27.7与项目使用的Expo SDK版本兼容(该版本对应Expo SDK 50),若SDK版本不匹配需调整包版本。 - 测试前后台接收情况:分别测试应用在前台、后台、完全退出时的推送接收情况,若仅后台无法接收,需确认设备的后台刷新权限已开启。
- 检查设备网络环境:推送通知需要稳定的网络连接,确保设备处于蜂窝数据或Wi-Fi环境中,无网络限制。
内容的提问来源于stack exchange,提问作者Clarkie
相关产品推荐
相关产品推荐

