React Native推送通知 #

概述 #

推送通知是移动应用的重要功能,用于向用户发送消息提醒。本章节介绍如何在 React Native 中实现本地通知和远程推送。

权限配置 #

iOS 配置 #

  1. 在 Apple Developer 中启用 Push Notifications 能力
  2. 在 Xcode 中启用 Push Notifications
  3. ios/MyApp/Info.plist 中添加:
xml
<key>UIBackgroundModes</key>
<array>
  <string>remote-notification</string>
</array>

Android 配置 #

android/app/src/main/AndroidManifest.xml 中添加:

xml
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

<application ...>
  <meta-data
    android:name="com.google.firebase.messaging.default_notification_icon"
    android:resource="@drawable/ic_notification" />
  <meta-data
    android:name="com.google.firebase.messaging.default_notification_color"
    android:resource="@color/notification_color" />
</application>

notifee #

推荐的通知库,支持 iOS 和 Android。

安装 #

bash
npm install @notifee/react-native
cd ios && pod install

基本使用 #

tsx
import notifee, {AndroidImportance} from '@notifee/react-native';
import React, {useState} from 'react';
import {View, Button, StyleSheet} from 'react-native';

const NotificationExample = () => {
  const requestPermission = async () => {
    const settings = await notifee.requestPermission();
    console.log('Permission settings:', settings);
  };

  const displayNotification = async () => {
    await notifee.displayNotification({
      title: '通知标题',
      body: '这是通知内容',
      android: {
        channelId: 'default',
        importance: AndroidImportance.HIGH,
        pressAction: {
          id: 'default',
        },
      },
    });
  };

  return (
    <View style={styles.container}>
      <Button title="请求权限" onPress={requestPermission} />
      <Button title="发送通知" onPress={displayNotification} />
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    gap: 16,
  },
});

export default NotificationExample;

创建通知渠道(Android) #

tsx
import notifee, {AndroidImportance, AndroidVisibility} from '@notifee/react-native';

const createChannels = async () => {
  await notifee.createChannel({
    id: 'default',
    name: '默认通知',
    importance: AndroidImportance.HIGH,
    visibility: AndroidVisibility.PUBLIC,
  });

  await notifee.createChannel({
    id: 'messages',
    name: '消息通知',
    importance: AndroidImportance.HIGH,
    vibration: true,
    sound: 'default',
  });

  await notifee.createChannel({
    id: 'silent',
    name: '静默通知',
    importance: AndroidImportance.LOW,
    sound: undefined,
  });
};

带图片的通知 #

tsx
const displayImageNotification = async () => {
  await notifee.displayNotification({
    title: '图片通知',
    body: '这是一条带图片的通知',
    android: {
      channelId: 'default',
      largeIcon: 'https://example.com/image.png',
      style: {
        type: AndroidStyle.BIGPICTURE,
        picture: 'https://example.com/big-image.png',
      },
    },
    ios: {
      attachments: [
        {
          url: 'https://example.com/image.png',
        },
      ],
    },
  });
};

带操作的通知 #

tsx
const displayActionNotification = async () => {
  await notifee.displayNotification({
    title: '新消息',
    body: '您有一条新消息',
    android: {
      channelId: 'messages',
      actions: [
        {
          title: '回复',
          pressAction: {
            id: 'reply',
          },
          input: true,
        },
        {
          title: '标记已读',
          pressAction: {
            id: 'mark-read',
          },
        },
      ],
    },
  });
};

监听通知事件 #

tsx
import notifee, {EventType, Event} from '@notifee/react-native';
import {useEffect} from 'react';

const NotificationHandler = () => {
  useEffect(() => {
    const unsubscribe = notifee.onForegroundEvent(({type, detail}) => {
      switch (type) {
        case EventType.DISMISSED:
          console.log('用户关闭了通知');
          break;
        case EventType.PRESS:
          console.log('用户点击了通知', detail.notification);
          break;
        case EventType.ACTION_PRESS:
          console.log('用户点击了操作按钮', detail.pressAction);
          break;
      }
    });

    return unsubscribe;
  }, []);

  useEffect(() => {
    notifee.onBackgroundEvent(async ({type, detail}) => {
      if (type === EventType.PRESS) {
        console.log('后台点击通知', detail.notification);
      }
    });
  }, []);

  return null;
};

定时通知 #

tsx
import notifee, {TimestampTrigger, TriggerType} from '@notifee/react-native';

const scheduleNotification = async () => {
  const trigger: TimestampTrigger = {
    type: TriggerType.TIMESTAMP,
    timestamp: Date.now() + 5000, // 5秒后
  };

  await notifee.createTriggerNotification(
    {
      title: '定时通知',
      body: '这是5秒后的定时通知',
      android: {
        channelId: 'default',
      },
    },
    trigger,
  );
};

const scheduleRepeatNotification = async () => {
  const trigger: TimestampTrigger = {
    type: TriggerType.TIMESTAMP,
    timestamp: Date.now() + 60000,
    repeatFrequency: RepeatFrequency.HOURLY,
  };

  await notifee.createTriggerNotification(
    {
      title: '重复通知',
      body: '每小时提醒一次',
      android: {
        channelId: 'default',
      },
    },
    trigger,
  );
};

Firebase Cloud Messaging (FCM) #

用于远程推送通知。

安装 #

bash
npm install @react-native-firebase/app @react-native-firebase/messaging
cd ios && pod install

配置 Firebase #

  1. 在 Firebase Console 创建项目
  2. 添加 iOS 和 Android 应用
  3. 下载配置文件:
    • iOS: GoogleService-Info.plist 放入 ios/ 目录
    • Android: google-services.json 放入 android/app/ 目录

Android 配置 #

android/build.gradle 中添加:

gradle
dependencies {
  classpath 'com.google.gms:google-services:4.3.15'
}

android/app/build.gradle 中添加:

gradle
apply plugin: 'com.google.gms.google-services'

获取 FCM Token #

tsx
import messaging from '@react-native-firebase/messaging';
import {useEffect, useState} from 'react';

const FCMExample = () => {
  const [fcmToken, setFcmToken] = useState<string | null>(null);

  useEffect(() => {
    const getToken = async () => {
      const authStatus = await messaging().requestPermission();
      const enabled =
        authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
        authStatus === messaging.AuthorizationStatus.PROVISIONAL;

      if (enabled) {
        const token = await messaging().getToken();
        setFcmToken(token);
        console.log('FCM Token:', token);
      }
    };

    getToken();

    const unsubscribe = messaging().onTokenRefresh(token => {
      setFcmToken(token);
      console.log('Token refreshed:', token);
    });

    return unsubscribe;
  }, []);

  return (
    <View>
      <Text>FCM Token: {fcmToken}</Text>
    </View>
  );
};

监听消息 #

tsx
import messaging from '@react-native-firebase/messaging';
import {useEffect} from 'react';

const MessageHandler = () => {
  useEffect(() => {
    const unsubscribe = messaging().onMessage(async remoteMessage => {
      console.log('前台收到消息:', remoteMessage);
      
      await notifee.displayNotification({
        title: remoteMessage.notification?.title,
        body: remoteMessage.notification?.body,
        android: {
          channelId: 'default',
        },
      });
    });

    return unsubscribe;
  }, []);

  useEffect(() => {
    messaging().onNotificationOpenedApp(remoteMessage => {
      console.log('后台打开应用:', remoteMessage);
    });

    messaging()
      .getInitialNotification()
      .then(remoteMessage => {
        if (remoteMessage) {
          console.log('退出状态打开应用:', remoteMessage);
        }
      });
  }, []);

  useEffect(() => {
    messaging().setBackgroundMessageHandler(async remoteMessage => {
      console.log('后台收到消息:', remoteMessage);
    });
  }, []);

  return null;
};

订阅主题 #

tsx
const subscribeToTopic = async (topic: string) => {
  await messaging().subscribeToTopic(topic);
  console.log(`已订阅主题: ${topic}`);
};

const unsubscribeFromTopic = async (topic: string) => {
  await messaging().unsubscribeFromTopic(topic);
  console.log(`已取消订阅主题: ${topic}`);
};

// 使用示例
await subscribeToTopic('news');
await subscribeToTopic('sports');

完整示例 #

tsx
import React, {useEffect, useState} from 'react';
import {
  View,
  Text,
  Button,
  StyleSheet,
  Alert,
  ScrollView,
} from 'react-native';
import notifee, {
  AndroidImportance,
  EventType,
  TimestampTrigger,
  TriggerType,
} from '@notifee/react-native';
import messaging from '@react-native-firebase/messaging';

const NotificationService = () => {
  const [fcmToken, setFcmToken] = useState<string | null>(null);

  useEffect(() => {
    initNotifications();
    setupListeners();
  }, []);

  const initNotifications = async () => {
    await notifee.createChannel({
      id: 'default',
      name: '默认通知',
      importance: AndroidImportance.HIGH,
    });

    const authStatus = await messaging().requestPermission();
    const enabled =
      authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
      authStatus === messaging.AuthorizationStatus.PROVISIONAL;

    if (enabled) {
      const token = await messaging().getToken();
      setFcmToken(token);
    }
  };

  const setupListeners = () => {
    notifee.onForegroundEvent(({type, detail}) => {
      handleNotificationEvent(type, detail);
    });

    notifee.onBackgroundEvent(async ({type, detail}) => {
      handleNotificationEvent(type, detail);
    });

    messaging().onMessage(async remoteMessage => {
      await notifee.displayNotification({
        title: remoteMessage.notification?.title,
        body: remoteMessage.notification?.body,
        android: {channelId: 'default'},
      });
    });

    messaging().onNotificationOpenedApp(remoteMessage => {
      handleDeepLink(remoteMessage.data);
    });
  };

  const handleNotificationEvent = (type: EventType, detail: any) => {
    switch (type) {
      case EventType.PRESS:
        console.log('通知被点击', detail.notification);
        break;
      case EventType.ACTION_PRESS:
        console.log('操作按钮被点击', detail.pressAction);
        break;
    }
  };

  const handleDeepLink = (data: any) => {
    if (data?.screen) {
      // 导航到指定页面
      console.log('Navigate to:', data.screen);
    }
  };

  const sendLocalNotification = async () => {
    await notifee.displayNotification({
      title: '本地通知',
      body: '这是一条本地通知',
      android: {
        channelId: 'default',
        importance: AndroidImportance.HIGH,
      },
    });
  };

  const scheduleNotification = async () => {
    const trigger: TimestampTrigger = {
      type: TriggerType.TIMESTAMP,
      timestamp: Date.now() + 5000,
    };

    await notifee.createTriggerNotification(
      {
        title: '定时通知',
        body: '5秒后的提醒',
        android: {channelId: 'default'},
      },
      trigger,
    );

    Alert.alert('成功', '已设置5秒后的通知');
  };

  const cancelAllNotifications = async () => {
    await notifee.cancelAllNotifications();
    Alert.alert('成功', '已取消所有通知');
  };

  return (
    <ScrollView style={styles.container}>
      <Text style={styles.title}>推送通知服务</Text>

      <View style={styles.section}>
        <Text style={styles.label}>FCM Token:</Text>
        <Text style={styles.token}>{fcmToken || '获取中...'}</Text>
      </View>

      <View style={styles.buttons}>
        <Button
          title="发送本地通知"
          onPress={sendLocalNotification}
        />
        <Button
          title="设置定时通知"
          onPress={scheduleNotification}
        />
        <Button
          title="取消所有通知"
          onPress={cancelAllNotifications}
          color="#FF3B30"
        />
      </View>
    </ScrollView>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#f5f5f5',
    padding: 16,
  },
  title: {
    fontSize: 24,
    fontWeight: 'bold',
    textAlign: 'center',
    marginBottom: 24,
  },
  section: {
    backgroundColor: '#fff',
    borderRadius: 8,
    padding: 16,
    marginBottom: 16,
  },
  label: {
    fontSize: 14,
    color: '#666',
    marginBottom: 8,
  },
  token: {
    fontSize: 12,
    color: '#333',
    fontFamily: 'monospace',
  },
  buttons: {
    gap: 12,
  },
});

export default NotificationService;

总结 #

推送通知要点:

  • notifee:推荐的通知库,功能丰富
  • 通知渠道:Android 需要创建渠道
  • 通知事件:监听用户交互
  • 定时通知:支持延迟和重复
  • FCM:远程推送通知
  • 主题订阅:群发消息

继续学习 原生模块,了解如何与原生代码交互。

最后更新:2026-03-28