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

Expo Push Notifications仅前台显示,后台无推送问题求助

Expo推送通知仅前台显示,后台无推送问题排查

问题现象

  • 自身Expo应用调用Expo Push Notification V2 API发送推送时,通知仅在应用前台显示,后台完全无法接收
  • 用Postman或Expo官方推送测试工具发送同一token的通知时,后台可正常接收推送

已完成的排查动作

  • 确认后台通知权限、代码配置无误,设备系统设置允许后台通知
  • 检查应用内API实现,确认请求参数、响应处理均正确
  • 关闭设备省电模式,更换多台物理设备测试
  • 构建独立APK测试,问题依旧存在
  • 已严格遵循Expo官方推送通知配置指南

应用核心代码

async function registerForPushNotificationsAsync() {
    let token;
    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()).data;
    } else {
        alert("Must use physical device for Push Notifications");
    }

    if (Platform.OS === "android") {
        Notifications.setNotificationChannelAsync("default", {
            name: "default",
            importance: Notifications.AndroidImportance.MAX,
            vibrationPattern: [0, 250, 250, 250],
            lightColor: "#FF231F7C",
        });
    }

    return token;
}

async function sendPushNotification(
    expoPushToken: any,
    type?: string,
    name?: string
) {

    const message = {
        to: expoPushToken,
        sound: "default",
        title: "HI",
        body: "BY",
    };

    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),
    });
}

export default function App() {
    const { isLoadingComplete, user, error, refresh } = useCachedResources();
    const [notifications, setNotifications] = useState<Notification[]>([]);
    const [expoPushToken, setExpoPushToken] = useState<any>();
    const [notification, setNotification] = useState<any>(false);
    const notificationListener = useRef<any>();
    const responseListener = useRef<any>();

    useEffect(() => {
        registerForPushNotificationsAsync().then((token) =>
            setExpoPushToken(token)
        );
        console.log("eee", expoPushToken);
        notificationListener.current =
            Notifications.addNotificationReceivedListener((notification) => {
                setNotification(notification);
            });

        responseListener.current =
            Notifications.addNotificationResponseReceivedListener(
                async (response) => {
                    await Notifications.setBadgeCountAsync(0);
                    navRef.navigate("Dashboard", {
                        screen: "Messages",
                    });
                }
            );

        return () => {
            Notifications.removeNotificationSubscription(
                notificationListener.current
            );
            Notifications.removeNotificationSubscription(responseListener.current);
        };
    }, []);

    useEffect(() => {
        const newNotifications = notifications?.filter(
            (not) => not.status === "new"
        );

        newNotifications?.forEach(async (not) => {
            await sendPushNotification(expoPushToken, not.type, not.by.name);
            updateNotification(not.id!!, {
                status: "old",
            });
        });
    }, [notifications]);

    useEffect(() => {
        // getFCMToken();
        let unsubscribeNotification: Unsubscribe;
        if (user && user.id) {
            unsubscribeNotification = subscribeNotification(
                user.id,
                handleNotificationChange
            );
        }
        return () => {
            if (unsubscribeNotification) {
                unsubscribeNotification();
            }
        };
    }, [user]);

    const handleNotificationChange = (snapshot: QuerySnapshot<Notification>) => {
        let result: Notification[] = [];
        snapshot.forEach((doc) => {
            result.push({ ...doc.data(), id: doc.id });
        });

        setNotifications(result);
    };
}

排查建议

  1. 补充请求头的平台标识字段
    当前请求头缺少Expo-Platform字段(指定iOS/Android),Postman测试时可能自动带上了该字段,部分场景下后台推送需要它匹配设备类型。尝试在请求头添加:

    Expo-Platform: android
    

    或根据设备动态设置对应值。

  2. 明确推送消息的优先级与平台配置
    Android后台推送需设置priority为high,iOS需开启contentAvailable(普通通知也建议明确配置)。修改message对象:

    const message = {
      to: expoPushToken,
      sound: "default",
      title: "HI",
      body: "BY",
      priority: "high",
      contentAvailable: true,
      android: {
        priority: "high",
        channelId: "default" // 与创建的通知渠道ID保持一致
      }
    };
    
  3. 校验推送时的token有效性
    监听notifications变化的useEffect可能在expoPushToken初始化完成前就调用sendPushNotification,导致发送无效token。添加前置判断:

    if (!expoPushToken) return;
    await sendPushNotification(expoPushToken, not.type, not.by.name);
    
  4. 查看Expo推送API的响应结果
    当前代码未处理API响应,无法知晓Expo服务器是否返回错误。修改sendPushNotification打印响应:

    const response = await fetch("https://exp.host/--/api/v2/push/send", {
      // 现有配置
    });
    const responseData = await response.json();
    console.log("推送响应:", responseData);
    

    若存在token无效、消息格式错误等问题,可直接定位。

  5. 确认Android通知渠道的绑定
    确保发送消息时指定了channelId,否则系统可能使用默认渠道,而默认渠道的重要性可能不足以触发后台通知。

内容的提问来源于stack exchange,提问作者Meer Suhana

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 18:35:03