You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

获取到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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 21:30:31