news 2026/9/11 9:39:52

Expo 通知实战:expo-notifications 推送落地、状态矩阵与深度链接

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Expo 通知实战:expo-notifications 推送落地、状态矩阵与深度链接

Expo 通知实战:expo-notifications 推送落地、状态矩阵与深度链接

【免费下载链接】expoAn open-source framework for making universal native apps with React. Expo runs on Android, iOS, and the web.项目地址: https://gitcode.com/GitHub_Trending/ex/expo

Expo 通知基于expo-notifications模块,在 React Native 项目内同时覆盖 Android 与 iOS 的本地通知和远程推送。本文给出一套可落地的实践路径:从依赖安装、app.json插件配置、权限请求、Expo 推送令牌获取,到 FCM 与 APNs 凭据配置、EAS Build 构建、前/后台与关闭状态的行为控制,以及基于data字段的深度链接。

能力边界:本地通知与远程推送的差异

expo-notifications把两套能力装进同一套 API:

  • 本地通知:由应用进程自行调度,依赖expo-notify之外的定时或事件触发,不需要推送令牌,应用被系统终止后无法继续投递。
  • 远程推送:消息从服务端经 FCM 或 APNs 下发到设备,应用可以不在前台,甚至处于关闭状态;需要 Expo 推送令牌(ExponentPushToken[...]格式)作为投递地址。

应用不同状态下的行为差异(状态矩阵):

应用状态AndroidiOS
前台系统横幅 + 触发addNotificationReceivedListener默认无横幅、无声音,仅触发addNotificationReceivedListenersetNotificationHandler可开启横幅与声音
后台系统横幅 + 通知中心系统横幅 + 通知中心
已关闭系统投递到通知中心,点击触发addNotificationResponseReceivedListener系统直接投递到锁屏与通知中心,点击后冷启动应用并触发响应监听器

统一 API 覆盖了setNotificationHandlerrequestPermissionsAsyncgetExpoPushTokenAsyncsetNotificationChannelAsync等调用,上层代码无需区分平台;只有凭据准备和渠道行为仍需按平台处理。

跑通第一条推送通知:依赖安装、插件配置与最小代码

在一个现成的 Expo 应用里,先用以下命令补齐三个依赖:

npx expo install expo-notifications expo-device expo-constants

expo-notifications负责通知的注册、调度与展示,expo-device用于区分真机与模拟器,expo-constants用来读取projectId。接着在app.jsonexpo.plugins中加入"expo-notifications",prebuild 时原生侧的通知配置会被自动写入,不需要手工改AndroidManifest.xml或 iOS 的Entitlements文件。

最小可运行的注册与发送逻辑如下,包含权限请求、Expo 推送令牌获取、监听器和通过 Expo 推送接口发送测试消息:

import { useEffect, useState } from 'react'; import { View, Text, Button } from 'react-native'; import * as Device from 'expo-device'; import * as Notifications from 'expo-notifications'; import Constants from 'expo-constants'; Notifications.setNotificationHandler({ handleNotification: async () => ({ shouldShowBanner: true, shouldShowList: true, shouldPlaySound: true, shouldSetBadge: true, }), }); async function getToken() { const { status } = await Notifications.getPermissionsAsync(); if (status !== 'granted') { const r = await Notifications.requestPermissionsAsync(); if (r.status !== 'granted') return null; } const projectId = Constants.expoConfig?.extra?.eas?.projectId ?? Constants.easConfig?.projectId; const token = await Notifications.getExpoPushTokenAsync({ projectId }); return token.data; } async function send(token) { await fetch('https://exp.host/--/api/v2/push/send', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ to: token, title: '测试标题', body: '来自 Expo 推送的第一条通知', data: { screen: 'home' }, }), }); } export default function App() { const [token, setToken] = useState(''); const [last, setLast] = useState(null); useEffect(() => { if (!Device.isDevice) return; getToken().then(setToken); const onReceive = Notifications.addNotificationReceivedListener( (n) => setLast(n) ); const onTap = Notifications.addNotificationResponseReceivedListener( (r) => console.log('clicked', r) ); return () => { onReceive.remove(); onTap.remove(); }; }, []); return ( <View style={{ flex: 1, justifyContent: 'center', padding: 24 }}> <Text>{token}</Text> <Text>{last?.request.content.body}</Text> <Button title="发送测试通知" onPress={() => send(token)} disabled={!token} /> </View> ); }

验证方式:在真机上运行应用,确认界面打印出ExponentPushToken[...],随后点按按钮触发发送。前台会走handleNotification逻辑弹出横幅;把应用切到后台再发送,则走系统横幅与通知中心。

生产部署:FCM 与 APNs 凭据配置和 EAS Build 构建

Android 侧走 Firebase Cloud Messaging:先在 Firebase 控制台创建项目,拿到google-services.json,再通过eas credentials把该文件上传为项目的 Android 凭据。构建脚本参考 FCM 凭据官方配置文档。

iOS 侧走 APNs:在 Apple Developer 账号下为应用标识开启 Push Notifications,导出.p8格式的 APNs 密钥,同样通过eas credentials配置为项目的 iOS 凭据,或写入eas.json的构建配置中。

两侧凭据就绪后执行eas build生成包含原生推送配置的应用包。需要注意两个限制:Expo 推送令牌只能在真机上获取,模拟器不产生有效令牌;推送通道的完整验证(前后台投递、锁屏展示)也必须在真机完成,模拟器上不会收到远程推送。

通知生命周期:从发起到点击,监听器与深度链接

按时间线拆解一次推送的完整过程:

  • 发起:服务端POSThttps://exp.host/--/api/v2/push/send,payload 至少包含to(推送令牌)、titlebody,可选soundbadge与自定义data对象。
  • 到达前台:系统把消息交给应用,Notifications.setNotificationHandler返回的布尔值决定shouldShowBannershouldShowListshouldPlaySoundshouldSetBadge;同时addNotificationReceivedListener回调,参数notification.request.content包含titlebodydata
  • 后台/关闭:系统直接渲染横幅或锁屏通知,JS 侧不执行任何代码;用户点击后,addNotificationResponseReceivedListener被触发(应用已运行)或先冷启动再触发。
  • 点击响应:响应对象的notification.request.content.data携带发起时写入的自定义数据,是深度链接的入口:
Notifications.addNotificationResponseReceivedListener((response) => { const { screen, id } = response.notification.request.content.data ?? {}; if (screen) router.navigate(screen, { id }); });

应用从杀死状态被点击拉起时,getLastNotificationResponseAsync会返回最近一次点击的通知,用它可以在冷启动路径上恢复同样的页面跳转。

排错清单:通知不显示、令牌失败与渠道问题

通知不显示原因:系统级通知开关关闭、权限状态非granted、Android 渠道未创建,或发送请求实际返回了错误。 解决:核对系统设置与getPermissionsAsync返回值;在 Android 上先执行setNotificationChannelAsync;打印fetch响应体确认推送接口返回data: []而非错误对象。

令牌获取失败原因:在模拟器上调用getExpoPushTokenAsync,或未传入正确的projectId。 解决:确认Device.isDevicetrue再执行注册流程;projectIdConstants.expoConfig?.extra?.eas?.projectIdConstants.easConfig?.projectId读取,两者都缺失时检查eas.jsonapp.json的关联配置。

Android 通知不震动或静默送达原因:渠道的AndroidImportance设置过低,或根本未创建渠道。 解决:显式调用setNotificationChannelAsync并设置importance(如MAX),必要时配置vibrationPatternlightColor;Android 8.0 及以上所有通知都必须归属某个渠道。

iOS 始终收不到远程推送原因:APNs 证书未配置、证书与应用签名 Bundle ID 不一致。 解决:重新核对eas credentials中上传的 APNs 密钥,确认其对应的 App ID 与构建包签名完全一致。

点击通知无法跳转到目标页原因:data字段未写入,或冷启动路径未处理。 解决:发送时始终携带data;冷启动分支里补一次getLastNotificationResponseAsync检查。

参考文档

更完整的平台差异、凭据细节与边界情况,见 推送通知设置文档、接收通知文档 与 推送通知 FAQ。

【免费下载链接】expoAn open-source framework for making universal native apps with React. Expo runs on Android, iOS, and the web.项目地址: https://gitcode.com/GitHub_Trending/ex/expo

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/11 9:38:06

spdlog C++日志库完整实战:从基础API到MFC集成

之前在做 C 项目日志模块时&#xff0c;反复纠结于是用 printf 打点、OutputDebugString 输出&#xff0c;还是自己封装一个文件日志类。前两者功能太弱&#xff0c;自研的轮转、分级、线程安全都要从零实现&#xff0c;费时费力还容易埋坑。后来换上了 spdlog&#xff0c;整个…

作者头像 李华
网站建设 2026/9/3 1:42:33

Proval:自托管代码审查代理,让 MR/PR 审查数据留在内网

这次我们来看一个自托管开发工具&#xff1a;Proval。它本质上是一个代码审查代理&#xff08;code review agent&#xff09;&#xff0c;服务端部署在你自己的环境里&#xff0c;然后接入 GitLab、Forgejo、GitHub 三个主流 Git 代码托管平台。Show HN 这类项目通常更适合关注…

作者头像 李华
网站建设 2026/9/4 15:32:42

Joplin笔记应用:5平台一键同步,数据彻底握在自己手里

Joplin笔记应用&#xff1a;5平台一键同步&#xff0c;数据彻底握在自己手里 【免费下载链接】joplin Joplin - the privacy-focused note taking app with sync capabilities for Windows, macOS, Linux, Android and iOS. 项目地址: https://gitcode.com/GitHub_Trending/j…

作者头像 李华
网站建设 2026/9/4 14:44:27

如何挑选 Remotion 模板:从 0 到成片的实用指南

如何挑选 Remotion 模板&#xff1a;从 0 到成片的实用指南 【免费下载链接】remotion &#x1f3a5; Make videos programmatically with React 项目地址: https://gitcode.com/GitHub_Trending/re/remotion Remotion 是一款用 React 写视频的工具&#xff0c;官方仓库…

作者头像 李华
网站建设 2026/9/4 14:34:42

从滴滴运维真题看Linux笔试高频考点与故障排查思路

1. 这份真题&#xff1a;一份值得细读的运维能力画像 前两天整理电脑资料&#xff0c;翻出一份2017年滴滴出行秋招运维岗的笔试题目&#xff0c;来回看了几遍&#xff0c;觉得挺有意思。那会儿正值网约车业务快速扩张期&#xff0c;滴滴的运维团队承担着非常大的线上压力&#…

作者头像 李华