
首发于公众号 前端混合开发,欢迎关注。
推送通知已成为构建移动应用时需要考虑的重要功能。由于它们类似于短信,但发送不需要任何费用,许多企业现在更喜欢使用推送通知向应用用户发送信息和警报。
在这篇文章中,我们将看到如何在React Native应用中创建和发送推送通知。
推送通知是从应用程序发送到已安装该应用的用户的消息或警报。主要有两种类型的通知:
推送通知在移动应用开发世界中非常流行,原因有很多。例如:
这些优势使得推送通知对几乎所有类型的移动应用都非常有用,从手机游戏到电商应用等等。
在我们深入了解如何在 React Native 应用中实现推送通知的技术细节之前,理解React Native推送通知的工作原理可能会有所帮助。这里有一个图表,简化了通知服务如何与设备进行通信:

当涉及到在React Native中设置推送通知时,有几种设置方式:
下面我们更深入地了解这些方法,然后深入我们的演示。
Android和iOS平台都提供了用于接收推送通知的原生平台特定API
我们可以使用React Native Firebase库来在Android上集成FCM,使用 push-notification-ios 库来在iOS上集成APNs。
React Native Firebase 库也提供了一种通过 FCM 在iOS上发送推送通知的方法。可以从Node.js服务器通过 firebase-admin 和 node-apn 向注册的移动设备发送远程通知
FCM 和 APNs 都是特定平台的原生推送通知服务。如果我们直接使用这些原生推送通知服务,我们通常需要在应用的前端和后端使用不同的库。
由于这可能会带来不便,因此有几个云服务提供了使用统一源代码同时处理FCM和APNs的方法。一些流行的推送通知服务包括:
这些通知服务在原生推送通知系统之上提供了一个抽象层,通过一个托管的中间推送通知服务器,正如你在之前显示的图表中看到的那样。
像 Notifee 和 react-native-notifications 这样的库提供了原生模块,可以通过统一的库API轻松接收远程通知并显示本地通知。
你可以直接使用 FCM/APNs 或者使用这些库的托管推送通知服务。然而,请记住,我们必须在 Expo 中使用裸工作流来使用这些库,因为这些库不包含在 Expo 应用程序中。
要在React Native应用程序中使用推送通知,我们首先需要注册应用程序以获取推送通知令牌。这个令牌是一个长字符串,可以唯一标识每个设备。然后,我们将在服务器上的数据库中存储该令牌,发送通知,并处理我们发送的已接收到的通知。
在我们深入研究之前,我们将向一个已经开发的项目添加推送通知。这个项目是一个用于出售二手物品的电子商务React Native应用程序。使用现有的项目将使我们能够专注于我们演示的推送通知方面。
要将项目的源代码下载到你的电脑中,请在你的终端运行以下命令:
git clone https://github.com/codezri/Done-With-It-App.git接下来,我们将安装项目所需的依赖项,并启动React Native开发服务器:
yarn install 
yarn start 上述命令安装依赖项并启动Expo开发服务器,因此你可以通过在Android或iOS上使用Expo应用来测试你的应用程序。带有工作后端的示例应用如下所示:

接下来,我们将从React Native Expo获取推送通知令牌,以开始接收应用程序的通知。
记住,要在React Native应用程序中使用推送通知,我们首先需要注册应用程序以获取推送通知令牌。在这里,我们将使用Expo中的通知API。
为了做到这一点,让我们进入 navigation 目录和 AppNavigator 组件。在这里,我们将从Expo中获取一个令牌。让我们从下面的Expo获取 Notifications 函数:
import * as Notifications from 'expo-notifications';上述功能帮助我们请求用户权限以发送推送通知,并为特定设备接收一个独特的 Expo 通知令牌。
现在,我们将在 AppNavigator 组件中编写一个 async function ,它将从 React Native Expo 请求一个令牌:
async function registerForPushNotificationsAsync() {
    let token;
    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;
    console.log(token);
    return token;
}在上述代码中,我们使用 expo-notifications 模块来获取用户接收通知的权限。该函数等待接收通知权限 status 。
接下来,我们检查是否已授予权限。如果没有,我们会显示一个关于错误的警告,并立即从函数中 return 。如果令牌请求过程成功,我们将从函数中返回令牌。否则,目前,我们将Expo的 token 记录到控制台,以便于开发。
为了在我们的应用中调用上述函数,我们将使用来自React的 useEffect 钩子:
const AppNavigator = () => {
    useEffect(() => {
        registerForPushNotificationsAsync();
    }, []);在上述代码中,我们传递了从 React 导入的 useEffect Hook,并传递了一个名为 registerForPushNotificationsAsync 的函数,以确保它只被调用一次。
现在,通过Expo应用程序打开应用。一旦你打开应用,你可以在控制台上看到Expo推送通知令牌。
当新用户打开应用时,这个独特的令牌将会被生成,所以我们可以在服务器中存储这些令牌,并以编程方式向所有注册的设备发送通知。将令牌保存在某处——我们很快就会用它来测试通知。
我们可以通过添加推送通知令牌,使用Expo通知工具向设备发送测试通知。进入Expo通知工具,输入你的令牌,输入标题和描述,保持你的应用在后台,然后点击发送通知按钮来发送测试通知。
现在你可以在你的设备上看到通知,如下预览所示:

如果你需要在应用处于前台时显示通知,你可以在 AppNavigator.js 源文件中添加以下配置:
Notifications.setNotificationHandler({
  handleNotification: async () => ({
    shouldShowAlert: true
  }),
});
const AppNavigator = () => {
// ----
// ----既然我们已经手动测试了Expo通知,那么让我们在服务器中存储推送通知令牌,并以编程方式发送通知。
为了存储和使用我们服务器的推送通知,我们需要以一种可以注册新用户和设备的方式配置我们的应用程序用户界面。
为了实现这个,让我们进入我们项目的 api 目录并打开一个我们将命名为 expoPushTokens.js 的新文件。然后按照以下步骤操作:
import client from './client';
const register = (pushToken) => client.post('/expoPushTokens', { token: pushToken });
export default {
    register,
}在上述代码中,我们首先导入了客户端模块,该模块也在 api 目录中。
我们定义了一个函数 register ,它接受一个 pushToken 。
现在,我们将在后端的 /expoPushToken 上发布一个客户端或新用户到 url 。在请求的主体中,我们将添加一个设置为 pushToken 的对象 token 。然后,我们将以 register 方法导出为默认对象。
接下来,我们回到 AppNavigator 组件。我们将不再在控制台上记录我们的令牌,而是将其发送到服务器:
const AppNavigator = () => {
    useEffect(() => {
        registerForPushNotificationsAsync()
            .then(token => expoPushTokensApi.register(token));
    }, []);现在,我们正在将新用户发送去获取令牌,同时也将用户信息发送到我们的后端服务器。稍后,我们可以使用这些令牌向所有注册的设备发送通知。
要向服务器发送推送通知,我们需要使用Expo提供的一个SDK。如果你访问Expo的文档,你会找到关于如何在许多语言中实现服务器上的推送通知的信息。
在这个教程中,我将使用一个Node.js服务器。你可以查看这个GitHub仓库,这是我在这个教程中使用的服务器源代码。我们将访问服务器中的 utilities 目录,并在其中包含 Expo SDK。要做到这一点,我们首先需要做以下操作:
# # cd into the newly cloned Git repository.
git clone https://github.com/iamfortune/DoneWithIt-Backend.git
# Next we install the npm packages using this command:
npm install 接下来,将你的计算机的本地网络IP地址添加到React Native应用的 baseURL 和后端项目的 assetsBaseUrl 中。
然后我们用以下命令启动我们的开发服务器:
npm start 如果你将电脑和移动设备保持在同一网络中,你可以在React Native应用中看到一些预先包含的列表。
现在,前往后端项目中的 utilities/pushNotifications.js 文件。在那里,将 Expo SDK添加到包中:
npm i expo-server-sdk #install the package
//file name: utilities/pushNotifications.js
const { Expo } = require("expo-server-sdk");接下来,我们将编写一个函数,该函数将接收我们的推送通知——既包括推送令牌,也包括我们想要发送给用户的消息。然后,我们将创建一个新的块方法来处理推送通知:
const sendPushNotification = async (targetExpoPushToken, message) => {
  const expo = new Expo();
  const chunks = expo.chunkPushNotifications([
    { to: targetExpoPushToken, sound: "default", body: message }
  ]);现在,可以在服务器代码的任何地方调用 sendPushNotification 函数,根据 Expo 推送通知令牌向任何设备发送通知,如下所示:
const { Expo } = require('expo-server-sdk');
const sendPushNotification = require('../utilities/pushNotifications');
// ----
// ----
if (Expo.isExpoPushToken(expoPushToken)) {
  await sendPushNotification(expoPushToken, message);
}
// ----你可以在 routes/messages.js 文件中查看 sendPushNotification 函数的示例用法。接下来,让我们确定如何处理在React Native应用中收到的通知。
要处理接收到的通知,我们首先需要有一个事件监听器,每当用户点击通知时都会被调用。让我们在 AppNavigator 函数内部添加一个事件监听器,它接收 Notifications 对象:
const AppNavigator = () => {
    const responseListener = useRef();
    useEffect(() => {
        registerForPushNotificationsAsync()
            .then(token => expoPushTokensApi.register(token));
        // Works when app is foregrounded, backgrounded, or killed
        responseListener.current = Notifications.addNotificationResponseReceivedListener(response => {
            console.log('--- notification tapped ---');
            console.log(response);
            console.log('------');
        });
        // Unsubscribe from events
        return () => {
            Notifications.removeNotificationSubscription(responseListener.current);
        };
    }, []);
// ----
// ----你可以通过Expo推送通知工具发送测试通知,并点击收到的通知。一旦这样做,将在控制台上看到通知点击响应。
Expo notifications 包提供了一个监听器,如果应用程序在前台,它可以检测到接收到的通知事件。以下代码片段会在你的前台应用通过Expo推送通知系统接收到通知时,记录一个特定的通知对象:
// ----
// ----
Notifications.setNotificationHandler({
  handleNotification: async () => ({
    shouldShowAlert: true
  }),
});
const AppNavigator = () => {
    const notificationListener = useRef();
    useEffect(() => {
        registerForPushNotificationsAsync()
            .then(token => expoPushTokensApi.register(token));
        notificationListener.current = Notifications.addNotificationReceivedListener(notification => {
            console.log('--- notification received ---');
            console.log(notification);
            console.log('------');
        });
        // Unsubscribe from events
        return () => {
            Notifications.removeNotificationSubscription(notificationListener.current);
        };
    }, []);
// ----
// ----如果你的应用程序在前台,上述代码会记录通知对象。看看下面的预览:
你可以从这个GitHub仓库浏览这个React Native应用的完整源代码。同样,你也可以从这个GitHub仓库浏览完整的服务器端代码。
请注意,在这里,我没有设置 FCM 就收到了我的 Android 设备的通知,因为我在使用 Expo 应用进行开发。如果你需要在没有 Expo 应用的情况下测试你的应用,或者你希望将你的应用部署到 Google Play 或 Apple App Store,请确保正确生成 FCM 和 APNs 凭证。
官方的Expo文档可以指导你为生产应用设置FCM和APNs。然而,由于Expo应用,你可以在不配置FCM或APNs的情况下开发和测试你的应用程序。
在某些情况下,开发者不需要远程服务器来发送通知。一个例子可以是音乐播放器,当一首歌曲正在播放时,应用需要显示一个通知。
在某些情况下,开发者不需要远程服务器来发送通知。其中一个例子可以是音乐播放器,当播放音轨时,应用需要显示一个通知:

以下代码块展示了如何创建一个本地通知:
import { StyleSheet, Text, View, Button } from "react-native";
import * as Notifications from "expo-notifications";
{
  /*Configure our notification settings:*/
}
Notifications.setNotificationHandler({
  handleNotification: async () => ({
    shouldShowAlert: true,
    shouldPlaySound: true,
    shouldSetBadge: true,
  }),
});
const generateNotification = async () => {
  //show the notification to the user
  Notifications.scheduleNotificationAsync({
    //set the content of the notification
    content: {
      title: "Demo title",
      body: "Demo body",
    },
    trigger: null,
  });
};
return (
  <View style={styles.container}>
    {/*When clicked, execute the generateNotification function*/}
    <Button
      title="Generate notification"
      onPress={() => generateNotification()}
    />
  </View>
);这将是代码的结果:

除了Expo的通知服务,我们还可以使用 Notifee 来为我们生成推送通知。这个库拥有许多特性,其中包括:
首先,让我们创建一个空白的React Native项目,并安装 notifee 模块以开始使用 Notifee:
npx react-native@latest init notifeeLearn
npm install notifee然后,要使用这个库,在 App.tsx 文件中编写这段代码:
import notifee from "@notifee/react-native";
function App(): React.JSX.Element {
  // 定义我们的处理函数:
  async function onDisplayNotification() {
    // 请求权限(iOS 需要)
    await notifee.requestPermission();
    // 创建一个频道(Android 需要)
    const channelId = await notifee.createChannel({
      id: "default",
      name: "默认频道",
    });
    // 显示一个通知
    await notifee.displayNotification({
      title: "通知标题",
      body: "通知的主体内容",
      android: {
        channelId,
        // 如果你想要通知被按下时打开应用,需要 pressAction
        pressAction: {
          id: "default",
        },
      },
    });
  }
  return (
    <SafeAreaView style={styles.main}>
    {/*创建一个按钮,点击时,应向用户显示一个通知*/}
      <Button
        title="显示通知"
        onPress={() => onDisplayNotification()}
      />
    </SafeAreaView>
  );
}结果:

现在我们已经看到了 Notifee 的一个非常基本的实现,让我们来看一个更复杂的例子。
感谢 Notifee,我们甚至可以创建后台通知,即使应用程序关闭,也可以发送。这适用于我们想要发送一个无声通知的情况,无论应用程序是否打开,例如文本消息或已完成的下载,都需要发送。
作为第一步,我们必须配置后台事件。为了实现这一点,请导航到 index.js 文件。编写以下代码:
// 文件名:index.js
import notifee, {EventType} from '@notifee/react-native';
// 这个处理器将监听后台事件:
notifee.onBackgroundEvent(async ({type, detail}) => {
  const {notification, pressAction} = detail;
  // 记录通知数据
  console.log('类型 ', type);
  console.log('通知数据 ', detail);
  // 检查用户是否已按下通知
  if (type === EventType.PRESS && pressAction.id === 'default') {
    // 进行一些处理..
    console.log('默认按钮被按下');
    // 在事件被注册后移除通知。
    await notifee.cancelNotification(notification.id);
  }
});然后,当用户点击通知时,React会将他们带回应用程序,并打印出通知的 id 以及交互类型:

如前所述,我们甚至可以使用 Notifee 的交互式 API 配置我们的通知以使其具有交互性。为此,在你的 displayNotifications 函数中的 actions 数组添加一个 title 和一个 pressAction 字段:
// 文件名:App.tsx
// 为简洁起见,移除了不必要的代码
await notifee.displayNotification({
  //..后续代码..
  android: {
    //后续代码..
    pressAction: {
      /*更多代码*/
    },
    //在此配置动作:
    actions: [{ title: "点击我", pressAction: { id: "click-me" } }],
  },
});现在我们已经创建了一个动作,接下来我们需要创建一个前台服务处理程序:
// 文件名:App.tsx
useEffect(() => {
  // 注册我们的前台事件:
  notifee.onForegroundEvent(({ type, detail }) => {
    // 如果通知上的按钮被点击:
    if (type === EventType.ACTION_PRESS && detail?.pressAction?.id) {
      console.log("detail", detail);
      // 打印出通知的 id:
      console.log(
        "用户点击了一个带有 id 的动作:",
        detail.pressAction.id,
      );
    }
  });
}, []);结果:

有许多方法可以使用交互式通知。例如:
Expo 和 Notifee 都是执行相同任务的优秀开源库。此外,它们具有类似的功能集和学习曲线。因此,这意味着你不需要花费大量时间来学习这些库。
这里有一个比较这两个库的表格:
| 特性 | Expo Notifications | Notifee | 
|---|---|---|
| 通知类型 | 本地和远程通知 | 本地和远程通知 | 
| 整合 | FCM和APN | FCM和OneSignal | 
| 定制 | 有限的定制选项 | 更多自定义选项 | 
| 性能 | 高效且轻量级 | 略低于Expo通知的效率 | 
| 文档 | 体面的文档 | 优秀的文档 | 
| 社区 | 良好的社区支持 | 强大的社区支持 | 
| 需要Expo模块吗? | Yes | No | 
| 最佳使用案例 | 简单通知 | 复杂通知 | 
如果你想优先考虑效率并且只需要简单的本地和远程通知,Expo是理想的选择。然而,对于更自定义或复杂的通知,你可以考虑使用Notifee。
开发人员在使用 Expo 通知和 Notifee 时常常会遇到一些常见的问题。让我们看看这些问题的原因以及如何解决它们:
在这篇文章中,我们了解了为什么推送通知如此受欢迎。我们还学习了如何在 React Native 应用程序中发送推送通知,方法是添加通知令牌,从服务器发送它们,并使用 Expo 通知 API 在用户设备上显示它们。
此外,我们探索了如何通过Notifee库显示本地和交互式通知。这个库提供了定制推送通知或创建更复杂通知类型的方法。