戰(zhàn)教程:15分鐘讓通知真正送達(dá)用戶手機(jī))
Expo 推送通知實(shí)戰(zhàn)教程15分鐘讓通知真正送達(dá)用戶手機(jī)【免費(fèi)下載鏈接】expoAn open-source framework for making universal native apps with React. Expo runs on Android, iOS, and the web.項(xiàng)目地址: https://gitcode.com/GitHub_Trending/ex/expo訂單發(fā)貨了、好友點(diǎn)贊了、活動(dòng)開(kāi)始了——這些主動(dòng)找用戶的時(shí)刻靠的都是推送通知。這篇 Expo 通知expo-notifications教程面向新手手把手帶你從零跑通完整流程申請(qǐng)權(quán)限、拿到推送令牌、發(fā)出第一條推送、處理通知點(diǎn)擊。全程不需要寫(xiě)一行原生代碼15 分鐘左右就能在真機(jī)上收到一條由你親手發(fā)出去的通知。 先預(yù)覽最終效果完成本教程后你的應(yīng)用會(huì)具備三種能力啟動(dòng)時(shí)自動(dòng)申請(qǐng)通知權(quán)限并生成一個(gè)綁定設(shè)備的ExpoPushToken推送令牌點(diǎn)一下按鈕就能通過(guò) Expo 推送服務(wù)向這臺(tái)設(shè)備發(fā)送一條標(biāo)題 正文 自定義數(shù)據(jù)的推送無(wú)論應(yīng)用在前臺(tái)、后臺(tái)還是被用戶劃掉通知都能按預(yù)期呈現(xiàn)點(diǎn)擊通知還能跳回指定頁(yè)面。為什么 Expo 通知適合跨平臺(tái)項(xiàng)目推送通知?dú)v來(lái)是 React Native 開(kāi)發(fā)者最頭疼的部分Android 要配 FirebaseiOS 要申請(qǐng) APNs 證書(shū)兩端的 API 還各說(shuō)各話。Expo 通知模塊把這套復(fù)雜度封裝掉了核心價(jià)值有四點(diǎn)一套 API 雙端通用請(qǐng)求權(quán)限、獲取令牌、監(jiān)聽(tīng)事件Android 和 iOS 調(diào)用完全一致令牌即發(fā)即用ExpoPushToken一個(gè)令牌同時(shí)打通 FCM 與 APNs后端不需要分別對(duì)接兩家廠商配置插件自動(dòng)改原生把expo-notifications寫(xiě)進(jìn)app.json原生工程的通知相關(guān)設(shè)置由插件替你生成不用手改 Gradle 或 Podfile不鎖死推送服務(wù)想直接對(duì)接 FCM / APNs 也完全可以expo-notifications的 API 與具體推送服務(wù)解耦。 快速上手四步發(fā)出第一條推送第 1 步安裝依賴在項(xiàng)目根目錄執(zhí)行npx expo install expo-notifications expo-constantsexpo-notifications負(fù)責(zé)權(quán)限、令牌與通知的全部邏輯expo-constants用來(lái)從應(yīng)用配置里讀取projectId令牌需要綁定項(xiàng)目。第 2 步注冊(cè)配置插件在app.json或app.config.js中加入{ expo: { plugins: [ expo-notifications ] } }插件會(huì)在 prebuild / 構(gòu)建階段自動(dòng)寫(xiě)入 Android 與 iOS 的通知配置比如 Android 的通知圖標(biāo)、iOS 的 push 能力聲明。第 3 步寫(xiě)入最小可用代碼下面的示例覆蓋了完整鏈路前臺(tái)處理策略 → 權(quán)限 → 令牌 → 發(fā)送 → 監(jiān)聽(tīng)。直接替換你的App.tsx即可運(yùn)行import { useState, useEffect } from react; import { Text, View, Button, Platform } from react-native; import * as Notifications from expo-notifications; import Constants from expo-constants; // ① 聲明前臺(tái)收到通知時(shí)的呈現(xiàn)策略 Notifications.setNotificationHandler({ handleNotification: async () ({ shouldShowBanner: true, // 頂部橫幅 shouldShowList: true, // 進(jìn)入通知中心 shouldPlaySound: true, // 播放聲音 shouldSetBadge: true, // 更新應(yīng)用角標(biāo) }), }); // ② 請(qǐng)求權(quán)限并獲取 ExpoPushToken async function registerForPushNotificationsAsync() { // Android 8.0 必須顯式創(chuàng)建通知渠道否則通知可能被靜默 if (Platform.OS android) { await Notifications.setNotificationChannelAsync(default, { name: default, importance: Notifications.AndroidImportance.MAX, }); } // 先看現(xiàn)有權(quán)限未授權(quán)才彈窗請(qǐng)求避免打擾用戶 const { status: existing } await Notifications.getPermissionsAsync(); const { status } existing granted ? { status: granted as const } : await Notifications.requestPermissionsAsync(); if (status ! granted) return null; // 令牌綁定 projectId項(xiàng)目換賬號(hào)、改名后令牌依然有效 const projectId Constants.expoConfig?.extra?.eas?.projectId ?? Constants.easConfig?.projectId; return (await Notifications.getExpoPushTokenAsync({ projectId })).data; } // ③ 通過(guò) Expo 推送服務(wù)發(fā)送測(cè)試推送 async function sendPushNotification(token: string) { const payload { to: token, sound: default, title: Hello Expo!, body: 這是你的第一條 Expo 通知, data: { screen: detail, id: 123 }, // 自定義數(shù)據(jù)供深度鏈接使用 }; await fetch(https://exp.host/--/api/v2/push/send, { method: POST, headers: { Content-Type: application/json }, body: JSON.stringify(payload), }); } export default function App() { const [token, setToken] useState(); const [last, setLast] useStateNotifications.Notification(); useEffect(() { registerForPushNotificationsAsync().then(t setToken(t ?? )); // 前臺(tái)監(jiān)聽(tīng)JS 層第一時(shí)間感知通知到達(dá) const received Notifications.addNotificationReceivedListener(setLast); // 點(diǎn)擊監(jiān)聽(tīng)用戶點(diǎn)擊通知后觸發(fā)可在此做路由跳轉(zhuǎn) const tapped Notifications.addNotificationResponseReceivedListener(r console.log(通知被點(diǎn)擊:, r.notification.request.content.data), ); return () { received.remove(); tapped.remove(); }; }, []); return ( View style{{ flex: 1, alignItems: center, justifyContent: center }} Text推送令牌: {token}/Text Text最近收到: {last?.request.content.body}/Text Button title發(fā)送測(cè)試通知 disabled{!token} onPress{() sendPushNotification(token)} / /View ); }第 4 步配置憑據(jù)構(gòu)建并在真機(jī)測(cè)試推送需要平臺(tái)憑據(jù)兩端各做一次只需一次平臺(tái)憑據(jù)操作要點(diǎn)AndroidFCMFirebase創(chuàng)建 Firebase 項(xiàng)目、生成google-services.json再通過(guò)eas credentials上傳iOSAPNs需要 Apple Developer 賬號(hào)首次eas build時(shí)按提示啟用推送并生成 APNs 密鑰隨后構(gòu)建并測(cè)試eas build # 生成包含通知配置的 development build npx expo start # 啟動(dòng)開(kāi)發(fā)服務(wù)器安裝后打開(kāi)應(yīng)用拿到屏幕上顯示的令牌粘貼到 Expo 官網(wǎng)的推送測(cè)試工具中填寫(xiě)標(biāo)題正文點(diǎn)擊發(fā)送即可。?? 推送必須用真機(jī)驗(yàn)證Android 模擬器需帶 Google Play 服務(wù)iOS 模擬器需 Xcode 14 及以上。 機(jī)制拆解一條推送到底怎么送達(dá)三種應(yīng)用狀態(tài)三種行為同一條推送到達(dá)時(shí)的表現(xiàn)取決于應(yīng)用當(dāng)時(shí)處于什么狀態(tài)應(yīng)用狀態(tài)推送的呈現(xiàn)方式前臺(tái)完全由你的 JS 代碼接管setNotificationHandler決定橫幅/聲音/角標(biāo)addNotificationReceivedListener同步觸發(fā)后臺(tái) / 已被劃掉系統(tǒng)原生顯示通知應(yīng)用不參與渲染用戶點(diǎn)擊通知觸發(fā)addNotificationResponseReceivedListener應(yīng)用未運(yùn)行時(shí)iOS 端建議盡早注冊(cè)監(jiān)聽(tīng)并在啟動(dòng)時(shí)用getLastNotificationResponse()補(bǔ)查點(diǎn)擊動(dòng)作最后一個(gè)細(xì)節(jié)很關(guān)鍵從通知點(diǎn)進(jìn)來(lái)冷啟動(dòng)時(shí)監(jiān)聽(tīng)器可能還沒(méi)掛載完畢。所以除了注冊(cè)監(jiān)聽(tīng)最好在應(yīng)用啟動(dòng)時(shí)調(diào)用一次getLastNotificationResponse()確保點(diǎn)擊通知 → 跳轉(zhuǎn)對(duì)應(yīng)頁(yè)面的深度鏈接 100% 生效。無(wú)感后臺(tái)任務(wù)Headless 通知不是所有推送都需要彈給用戶看。只帶data字段、沒(méi)有標(biāo)題正文的推送稱為Headless 通知——系統(tǒng)不展示任何 UI而是喚醒一段 JS 任務(wù)默默處理數(shù)據(jù)比如靜默拉取內(nèi)容、寫(xiě)入存儲(chǔ)甚至再?gòu)椧粭l自定義的本地通知// 注冊(cè)一個(gè)后臺(tái)任務(wù)收到 headless 推送時(shí)靜默執(zhí)行 Notifications.registerTaskAsync(syncTask, { data: async ({ data }) { // 寫(xiě)本地存儲(chǔ)、請(qǐng)求接口、或展示本地通知 console.log(后臺(tái)收到數(shù)據(jù):, data); }, }); 進(jìn)階玩法定時(shí)與重復(fù)的本地通知——不用服務(wù)器本地就能排期// 5 分鐘后提醒一次 await Notifications.scheduleLocalNotificationAsync( { title: 休息一下, body: 該看看眼睛了 }, { seconds: 300 }, );Android 渠道精細(xì)管理——不同業(yè)務(wù)走不同渠道訂單、社交、營(yíng)銷用戶可以單獨(dú)關(guān)閉某一類await Notifications.setNotificationChannelAsync(order, { name: 訂單更新, importance: Notifications.AndroidImportance.HIGH, }); // 發(fā)送時(shí)在 payload 中指定 channelId: order自定義聲音與圖標(biāo)——把音頻文件放進(jìn)assets目錄在 payload 的sound字段引用文件名即可Android 圖標(biāo)與強(qiáng)調(diào)色可通過(guò)配置插件的 options 參數(shù)定制。直連 FCM / APNs——如果后端已經(jīng)在用 Firebase 或自建 APNs 通道無(wú)需切換到 Expo 推送服務(wù)expo-notifications客戶端能力照常使用詳見(jiàn) docs/pages/push-notifications/sending-notifications-custom.mdx。? 常見(jiàn)問(wèn)題速查模擬器上收不到推送正?,F(xiàn)象推送依賴系統(tǒng)級(jí)推送通道請(qǐng)換真機(jī)或符合條件的模擬器。令牌獲取失敗依次檢查是否真機(jī)、projectId是否正確注入、權(quán)限彈窗是否被拒絕拒絕后需引導(dǎo)用戶去系統(tǒng)設(shè)置開(kāi)啟。前臺(tái)收到推送卻不彈橫幅檢查setNotificationHandler是否返回了shouldShowBanner: true——前臺(tái)行為完全由它決定。Android 8.0 通知靜默大概率是通知渠道沒(méi)建或渠道 importance 過(guò)低。Android 用戶從系統(tǒng)設(shè)置強(qiáng)行停止后收不到這是 Android 系統(tǒng)限制無(wú)法通過(guò)代碼繞過(guò)建議在新手引導(dǎo)中說(shuō)明。更多問(wèn)題可查閱docs/pages/push-notifications/faq.mdx 延伸閱讀與相關(guān)資源資源說(shuō)明docs/pages/push-notifications/overview.mdx推送服務(wù)總覽docs/pages/push-notifications/what-you-need-to-know.mdx各類通知類型與三態(tài)行為詳解docs/pages/push-notifications/fcm-credentials.mdxAndroid FCM V1 憑據(jù)配置docs/pages/push-notifications/receiving-notifications.mdx接收與響應(yīng)通知packages/expo-notifications通知模塊源碼apps/notification-tester倉(cāng)庫(kù)內(nèi)置的通知測(cè)試應(yīng)用現(xiàn)在就動(dòng)手克隆倉(cāng)庫(kù)把上面的示例代碼貼進(jìn)你自己的項(xiàng)目按四步走一遍——權(quán)限、令牌、構(gòu)建、發(fā)送15 分鐘后你的手機(jī)會(huì)收到第一條由你完全掌控的 Expo 推送通知git clone https://gitcode.com/GitHub_Trending/ex/expo推送是留存利器先把能收到跑通再逐步加上渠道分級(jí)、深度鏈接和靜默同步。祝上線順利【免費(fèi)下載鏈接】expoAn open-source framework for making universal native apps with React. Expo runs on Android, iOS, and the web.項(xiàng)目地址: https://gitcode.com/GitHub_Trending/ex/expo創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考