实现 RN 离线推送

更新时间: 2026/09/02 15:45:01

为了提高消息送达率,网易云信即时通讯 SDK(简称 NIM SDK)可以与 APNs、FCM 及 Android 手机厂商的系统推送服务配合,在应用进入后台、进程被回收或 SDK 连接中断时发送离线推送。

适用场景

通过集成各移动端设备厂商的原生推送 SDK,与 React Native NIM SDK 搭配使用,实现离线推送功能。

触发离线推送的典型场景包括:

  • 应用被切换到后台,且 App 资源被系统回收时。
  • 用户主动关闭 App 时。
  • iOS 应用被切换到后台时。
  • 网络不稳定导致 NIM SDK 无法与服务器保持正常连接时。

支持范围

平台 推送通道 接入方式
Android 小米、华为、vivo、OPPO、魅族、FCM、荣耀 集成网易云信 nimpush React Native 原生模块及对应厂商 SDK
iOS APNs 集成 @react-native-community/push-notification-ios

本文所述 React Native 插件暂不包含 HarmonyOS 推送实现。

Demo 源码

点击 此处 访问 RN 离线推送 Demo 源码。

实现原理

React Native 离线推送由 JavaScript SDK、React Native 推送插件、厂商推送 SDK 和网易云信服务共同完成:

sequenceDiagram
    participant App as 应用
    participant SDK as NIM SDK
    participant Plugin as 推送插件
    participant Vendor as 厂商推送服务
    participant Server as 网易云信服务器

    App->>SDK: 1. 创建 NIM 实例,调用 setOfflinePushConfig
    SDK->>Plugin: 2. 调用 init / getDeviceInfo
    Plugin-->>SDK: 返回设备厂商信息
    SDK->>Server: 3. 登录请求(携带设备信息)
    Server-->>SDK: 4. 返回建议的推送通道
    SDK->>Plugin: 5. 调用 onLogin
    Plugin->>Vendor: 6. 获取推送 Token
    Vendor-->>Plugin: 7. 返回 Token
    Plugin-->>SDK: 8. 上报 Token + 证书名称
    SDK->>Server: 9. 同步前后台状态

setOfflinePushConfig 必须在 V2NIMLoginService.login 之前调用。业务层不需要直接调用插件的 initonLogin 或自行上报 Token。

前提条件

根据本文操作前,请确保您已经完成以下操作:

  • 注册 IM 账号
  • 已创建可正常运行的 React Native Android 或 iOS 工程。
  • 已在目标厂商推送平台创建应用,并确保 Android applicationId、iOS Bundle Identifier、签名证书等信息与实际应用一致。
  • 使用真机测试。模拟器通常无法完整验证厂商推送或 APNs。

上传推送证书

  1. 在对应移动端设备厂商的推送平台上,注册应用获取应用信息,并开启推送服务。

  2. 将获取到的应用信息和推送证书,上传至 网易云信控制台,完成移动端设备厂商推送服务与网易云信服务的通信。

    具体步骤请参考以下文档:

引入推送资源

安装 RN NIM SDK

bashnpm install nim-web-sdk-ng@">=10"

React Native 工程需使用专用入口:

tsimport NIM from 'nim-web-sdk-ng/dist/v2/NIM_RN_SDK'

不要使用默认的浏览器入口 NIM_BROWSER_SDK

Android 推送插件

  1. 下载 React Native Android 推送插件

  2. 解压后将 nimpush/ 目录放到 React Native 工程的 android/ 目录下:

    textyour-react-native-app/
    ├── android/
    │   ├── app/
    │   ├── nimpush/
    │   │   ├── libs/
    │   │   ├── src/
    │   │   ├── build.gradle
    │   │   └── proguard-rules.pro
    │   └── settings.gradle
    └── package.json
    
  • npm install nim-web-sdk-ng 安装的 npm 包 不包含 nimpush 原生工程,需单独下载。
  • nimpush/libs 包含 rnpush.jar 及小米、vivo、OPPO 的厂商 SDK。
  • 建议将 NIM SDK 与同一发布周期提供的推送插件配套使用。

iOS 推送插件

iOS 使用社区插件:

bashnpm install @react-native-community/push-notification-ios
cd ios && pod install && cd ..

配置 Android 工程

仅需接入实际使用的厂商通道。例如只面向海外 Android 设备时,可以只配置 FCM。

引入 nimpush 模块

  1. android/settings.gradle 中添加:

    groovyinclude ':nimpush'
    project(':nimpush').projectDir = new File(rootProject.projectDir, 'nimpush')
    
  2. android/nimpush/libs 中的文件复制到 android/app/libs,然后在 android/app/build.gradle 中添加:

    groovydependencies {
        implementation project(':nimpush')
        implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
    }
    

rnpush.jar 是运行时依赖,必须保留。

注册 React Native Package

nimpush 采用手动 link,需要在 MainApplication 中注册 NIMPushPackage

Kotlin
kotlinimport com.netease.nim.rn.push.NIMPushPackage

class MainApplication : Application(), ReactApplication {
    override val reactNativeHost: ReactNativeHost =
        object : DefaultReactNativeHost(this) {
            override fun getPackages(): List<ReactPackage> =
                PackageList(this).packages.apply {
                    // nimpush 没有 npm autolinking 配置,需要手动注册。
                    add(NIMPushPackage())
                }
        }
}
Java
javaimport com.netease.nim.rn.push.NIMPushPackage;

@Override
protected List<ReactPackage> getPackages() {
    List<ReactPackage> packages = new PackageList(this).getPackages();
    // nimpush 没有 npm autolinking 配置,需要手动注册。
    packages.add(new NIMPushPackage());
    return packages;
}

添加厂商依赖

按需在 android/app/build.gradle 中添加:

通道 依赖
小米 插件包已提供 AAR
vivo 插件包已提供 AAR
OPPO 插件包已提供 AAR + Gson + Commons Codec
魅族 com.meizu.flyme.internet:push-internal:4.3.0
FCM play-services-base:18.5.0 + firebase-messaging:24.0.0
华为 com.huawei.hms:push:6.12.0.300
荣耀 com.hihonor.mcs:push:7.0.61.303

同时完成厂商配置文件接入:

通道 配置文件 Gradle 插件
FCM google-services.jsonandroid/app/ com.google.gms.google-services
华为 agconnect-services.jsonandroid/app/ com.huawei.agconnect
荣耀 mcs-services.jsonandroid/app/ 添加 https://developer.hihonor.com/repo

配置 AndroidManifest.xml

android/app/src/main/AndroidManifest.xml 中按需添加通道组件。

Android 13+ 需声明通知权限

xml<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
小米
xml<receiver
    android:name="com.netease.nimlib.mixpush.mi.MiPushReceiver"
    android:exported="true">
    <intent-filter android:priority="0x7fffffff">
        <action android:name="com.xiaomi.mipush.RECEIVE_MESSAGE" />
        <action android:name="com.xiaomi.mipush.MESSAGE_ARRIVED" />
        <action android:name="com.xiaomi.mipush.ERROR" />
    </intent-filter>
</receiver>
魅族

<application> 外添加:

xml<uses-permission android:name="com.meizu.flyme.push.permission.RECEIVE" />
<permission
    android:name="${applicationId}.push.permission.MESSAGE"
    android:protectionLevel="signature" />
<uses-permission android:name="${applicationId}.push.permission.MESSAGE" />
<uses-permission android:name="com.meizu.c2dm.permission.RECEIVE" />
<permission
    android:name="${applicationId}.permission.C2D_MESSAGE"
    android:protectionLevel="signature" />
<uses-permission android:name="${applicationId}.permission.C2D_MESSAGE" />

<application> 内添加:

xml<receiver
    android:name="com.netease.nimlib.mixpush.mz.MZPushReceiver"
    android:exported="true"
    android:permission="com.meizu.cloud.push.permission.MESSAGE">
    <intent-filter android:priority="0x7fffffff">
        <action android:name="com.meizu.flyme.push.intent.MESSAGE" />
        <action android:name="com.meizu.flyme.push.intent.REGISTER.FEEDBACK" />
        <action android:name="com.meizu.flyme.push.intent.UNREGISTER.FEEDBACK" />
        <action android:name="com.meizu.c2dm.intent.REGISTRATION" />
        <action android:name="com.meizu.c2dm.intent.RECEIVE" />
        <category android:name="${applicationId}" />
    </intent-filter>
</receiver>
vivo
xml<meta-data
    android:name="com.vivo.push.api_key"
    android:value="YOUR_VIVO_APP_KEY" />
<meta-data
    android:name="com.vivo.push.app_id"
    android:value="YOUR_VIVO_APP_ID" />

<receiver
    android:name="com.netease.nimlib.mixpush.vivo.VivoPushReceiver"
    android:exported="true">
    <intent-filter>
        <action android:name="com.vivo.pushclient.action.RECEIVE" />
    </intent-filter>
</receiver>

<service
    android:name="com.vivo.push.sdk.service.CommandClientService"
    android:exported="true"
    android:permission="com.push.permission.UPSTAGESERVICE" />
OPPO

<application> 外添加:

xml<uses-permission android:name="com.coloros.mcs.permission.RECIEVE_MCS_MESSAGE" />
<uses-permission android:name="com.heytap.mcs.permission.RECIEVE_MCS_MESSAGE" />

<application> 内添加:

xml<service
    android:name="com.netease.nimlib.mixpush.oppo.OppoPushService"
    android:exported="true"
    android:permission="com.coloros.mcs.permission.SEND_MCS_MESSAGE">
    <intent-filter>
        <action android:name="com.coloros.mcs.action.RECEIVE_MCS_MESSAGE" />
    </intent-filter>
</service>

<service
    android:name="com.netease.nimlib.mixpush.oppo.OppoAppPushService"
    android:exported="true"
    android:permission="com.heytap.mcs.permission.SEND_PUSH_MESSAGE">
    <intent-filter>
        <action android:name="com.heytap.mcs.action.RECEIVE_MCS_MESSAGE" />
        <action android:name="com.heytap.msp.push.RECEIVE_MCS_MESSAGE" />
    </intent-filter>
</service>

<service
    android:name="com.heytap.msp.push.service.CompatibleDataMessageCallbackService"
    android:exported="true"
    android:permission="com.coloros.mcs.permission.SEND_MCS_MESSAGE">
    <intent-filter>
        <action android:name="com.coloros.mcs.action.RECEIVE_MCS_MESSAGE" />
    </intent-filter>
</service>

<service
    android:name="com.heytap.msp.push.service.DataMessageCallbackService"
    android:exported="true"
    android:permission="com.heytap.mcs.permission.SEND_PUSH_MESSAGE">
    <intent-filter>
        <action android:name="com.heytap.mcs.action.RECEIVE_MCS_MESSAGE" />
        <action android:name="com.heytap.msp.push.RECEIVE_MCS_MESSAGE" />
    </intent-filter>
</service>
FCM
xml<service
    android:name="com.netease.nimlib.mixpush.fcm.FCMTokenService"
    android:exported="false">
    <intent-filter>
        <action android:name="com.google.firebase.MESSAGING_EVENT" />
    </intent-filter>
</service>
华为
xml<service
    android:name="com.netease.nimlib.mixpush.hw.HWPushService"
    android:exported="false">
    <intent-filter>
        <action android:name="com.huawei.push.action.MESSAGING_EVENT" />
    </intent-filter>
</service>
荣耀
xml<meta-data
    android:name="com.hihonor.push.app_id"
    android:value="YOUR_HONOR_APP_ID" />

<service
    android:name="com.netease.nimlib.mixpush.honor.HonorPushService"
    android:exported="false">
    <intent-filter>
        <action android:name="com.hihonor.push.action.MESSAGING_EVENT" />
    </intent-filter>
</service>

请求 Android 通知权限

Android 13+ 需在运行时请求通知权限:

tsimport { PermissionsAndroid, Platform } from 'react-native'

export async function requestNotificationPermission(): Promise<boolean> {
  if (Platform.OS !== 'android' || Platform.Version < 33) return true
  const result = await PermissionsAndroid.request(
    PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS
  )
  return result === PermissionsAndroid.RESULTS.GRANTED
}

修改原生配置后,必须重新构建 Android App

配置 iOS 工程

启用推送能力

在 Xcode 的 Target > Signing & Capabilities 中启用 Push Notifications。如需后台通知,再启用 Background Modes > Remote notifications。

确保以下标识保持一致:

  • Xcode Target 的 Bundle Identifier
  • Apple Developer 中 App ID 的 Bundle ID
  • APNs 证书对应的 App ID
  • 网易云信控制台中上传的 APNs 推送证书

修改 AppDelegate

以下示例适用于 Objective-C/Objective-C++ AppDelegate

  1. AppDelegate.h 中引入通知框架并实现 UNUserNotificationCenterDelegate

    • React Native 0.71 及以上示例:

      objc#import <UserNotifications/UNUserNotificationCenter.h>
      
      @interface AppDelegate : RCTAppDelegate <UNUserNotificationCenterDelegate>
      @end
      
    • React Native 0.70 及以下版本通常使用以下声明:

      objc@interface AppDelegate : UIResponder <UIApplicationDelegate, RCTBridgeDelegate, UNUserNotificationCenterDelegate>
      @end
      
  2. AppDelegate.mAppDelegate.mm 中引入:

    objc#import <UserNotifications/UserNotifications.h>
    #import <RNCPushNotificationIOS.h>
    
  3. 将以下委托方法合并到现有 AppDelegate 实现中:

    objc// 将 APNs 注册成功事件转发给 React Native 推送插件。
    - (void)application:(UIApplication *)application
        didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
    {
    [RNCPushNotificationIOS didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
    }
    
    // 将 APNs 注册失败事件转发给 React Native 推送插件。
    - (void)application:(UIApplication *)application
        didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
    {
    [RNCPushNotificationIOS didFailToRegisterForRemoteNotificationsWithError:error];
    }
    
    // 将后台远程通知转发给 React Native 推送插件。
    - (void)application:(UIApplication *)application
        didReceiveRemoteNotification:(NSDictionary *)userInfo
        fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
    {
    [RNCPushNotificationIOS didReceiveRemoteNotification:userInfo
                                    fetchCompletionHandler:completionHandler];
    }
    
    // 将用户点击通知的事件转发给 React Native 推送插件。
    - (void)userNotificationCenter:(UNUserNotificationCenter *)center
        didReceiveNotificationResponse:(UNNotificationResponse *)response
        withCompletionHandler:(void (^)(void))completionHandler
    {
    [RNCPushNotificationIOS didReceiveNotificationResponse:response];
    completionHandler();
    }
    
    // 控制应用位于前台时的通知展示方式。
    - (void)userNotificationCenter:(UNUserNotificationCenter *)center
        willPresentNotification:(UNNotification *)notification
        withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler
    {
    completionHandler(UNNotificationPresentationOptionSound |
                        UNNotificationPresentationOptionAlert |
                        UNNotificationPresentationOptionBadge);
    }
    
  4. 在现有 application:didFinishLaunchingWithOptions: 中设置通知中心代理,不要重复声明同名方法:

    objcUNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
    center.delegate = self;
    

实现离线推送

提供平台对应的推送插件

利用 React Native 的平台文件解析机制创建两个文件。

  • PushPlugin.android.ts

    tsimport { NativeModules } from 'react-native'
    
    export default NativeModules.NIMPushModule
    
  • PushPlugin.ios.ts

    tsimport PushNotificationIOS from '@react-native-community/push-notification-ios'
    
    export default PushNotificationIOS
    

业务代码统一引用 ./PushPlugin,React Native 会按当前平台选择对应文件。

创建 NIM 实例并设置推送配置

tsimport NIM from 'nim-web-sdk-ng/dist/v2/NIM_RN_SDK'
import PushPlugin from './PushPlugin'

const nim = NIM.getInstance(
  {
    appkey: 'YOUR_NIM_APP_KEY',
    apiVersion: 'v2'
  },
  {
    V2NIMLoginServiceConfig: {
      // 建议保持同一运行期内的设备标识稳定,减少重复设备连接。
      isFixedDeviceId: true,
      // Android 填 applicationId,iOS 填 Bundle Identifier。
      bundleName: 'YOUR_APPLICATION_ID_OR_BUNDLE_ID'
    }
  }
)

const offlinePushConfig = {
  miPush: {
    appId: 'YOUR_XIAOMI_APP_ID',
    appKey: 'YOUR_XIAOMI_APP_KEY',
    certificateName: 'YOUR_XIAOMI_CERTIFICATE_NAME'
  },
  hwPush: {
    appId: 'YOUR_HUAWEI_APP_ID',
    certificateName: 'YOUR_HUAWEI_CERTIFICATE_NAME'
  },
  vivoPush: {
    appId: 'YOUR_VIVO_APP_ID',
    appKey: 'YOUR_VIVO_APP_KEY',
    certificateName: 'YOUR_VIVO_CERTIFICATE_NAME'
  },
  oppoPush: {
    appId: 'YOUR_OPPO_APP_ID',
    appKey: 'YOUR_OPPO_APP_KEY',
    secret: 'YOUR_OPPO_APP_SECRET',
    certificateName: 'YOUR_OPPO_CERTIFICATE_NAME'
  },
  mzPush: {
    appId: 'YOUR_MEIZU_APP_ID',
    appKey: 'YOUR_MEIZU_APP_KEY',
    certificateName: 'YOUR_MEIZU_CERTIFICATE_NAME'
  },
  fcmPush: {
    certificateName: 'YOUR_FCM_CERTIFICATE_NAME'
  },
  honorPush: {
    appId: 'YOUR_HONOR_APP_ID',
    appKey: 'YOUR_HONOR_APP_KEY',
    certificateName: 'YOUR_HONOR_CERTIFICATE_NAME'
  },
  apns: {
    certificateName: 'YOUR_APNS_CERTIFICATE_NAME'
  }
}

// 必须在 NIM 实例创建后、登录前设置推送插件和证书配置。
nim.V2NIMSettingService.setOfflinePushConfig(PushPlugin, offlinePushConfig)

await nim.V2NIMLoginService.login('YOUR_ACCOUNT_ID', 'YOUR_ACCOUNT_TOKEN')
  • 只保留实际接入的通道配置。
  • 所有 certificateName 必须与网易云信控制台中的推送证书名称 完全一致(包括大小写)。
  • Android 插件从 JavaScript 配置读取小米、华为、OPPO、魅族的应用参数。vivo 和荣耀仍需要完成前文所述的 Android Manifest 或厂商配置文件接入,不能只填写 JavaScript 配置
  • 当前 React Native adapter 使用进程内存保存 SDK 的本地缓存。isFixedDeviceId: true 可以稳定同一运行期及重连期间的设备标识,但 不应将其视为跨应用冷启动永久不变的物理设备 ID

前后台状态处理

React Native 入口会自动将 AppState 注入 NIM SDK,SDK 自动监听 activeinactivebackground 状态并同步服务器。无需手动调用 setAppBackground

发送可离线推送的消息

发送消息时,确保没有显式关闭推送。也可以通过 pushConfig 指定推送文案:

tsconst receiverAccountId = 'RECEIVER_ACCOUNT_ID'
const conversationId = nim.V2NIMConversationIdUtil.p2pConversationId(receiverAccountId)
const message = nim.V2NIMMessageCreator.createTextMessage('hello world')

await nim.V2NIMMessageService.sendMessage(message, conversationId, {
  pushConfig: {
    pushEnabled: true,
    pushNickEnabled: true,
    pushContent: '您有一条新消息'
  }
})

如果会话或用户已开启免打扰,服务器会按照免打扰策略决定是否发送通知。

测试离线推送

建议准备两个 IM 账号和两台真机,按以下顺序验证:

  1. 接收端安装重新构建后的原生 App,并允许系统通知权限。
  2. 接收端登录 IM,确认 Android 日志出现插件初始化、设备信息和 Token 回调;iOS 确认已获得 APNs Device Token。
  3. Android 结束应用进程;iOS 先切到后台测试,再结束进程测试。
  4. 发送端发送启用了 pushEnabled 的消息。
  5. 检查接收端是否收到系统通知,验证冷启动、后台和断网场景。

Android 关键日志

textNIMPushModule: init
NIMPushModule: onPushToken
OfflinePushService:getDeviceInfo
OfflinePushService:: onLogin

日志中不应长期停留在 “获取设备信息” 或出现空 Token、空证书名称警告。

通知点击处理边界

Android nimpush 模块能识别通知产生的 Intent 并清理通知栏,但 未将点击 payload 暴露给 JavaScript

如需 “点击通知后跳转到指定会话”,需额外扩展:

  • pushPayload 中配置点击动作或 Intent。
  • 在 Android 原生层解析 Intent。
  • 扩展 Native Module,将会话参数发送到 JavaScript。

常见问题

问题 排查方向
NativeModules.NIMPushModuleundefined 检查 nimpush 目录是否存在、settings.gradle 是否包含 :nimpushMainApplication 是否注册 NIMPushPackage
Android 登录日志显示 getDeviceInfo timeout 插件未正确注册、Native Module 版本不匹配,或 getDeviceInfo 未返回合法 JSON
Android 未获得厂商 Token 检查厂商依赖是否打入 APK、Manifest 配置是否完整、厂商应用信息是否正确、配置文件是否位于 android/app/
日志提示证书名称为空 offlinePushConfig 中对应通道的 certificateName 为空或与控制台不一致
华为提示 Failed to check the Fingerprint 检查 keystore 是否与华为控制台及 agconnect-services.json 对应
iOS 无法获得 Device Token 检查是否使用真机、Xcode 是否启用 Push Notifications、Provisioning Profile 是否包含推送权限、AppDelegate 是否正确转发事件
一条消息出现重复通知 检查业务是否在收到在线消息时又调用了本地通知,同时服务器也发送了厂商推送
此文档是否对你有帮助?
有帮助
去反馈
  • 适用场景
  • 支持范围
  • Demo 源码
  • 实现原理
  • 前提条件
  • 上传推送证书
  • 引入推送资源
  • 安装 RN NIM SDK
  • Android 推送插件
  • iOS 推送插件
  • 配置 Android 工程
  • 引入 nimpush 模块
  • 注册 React Native Package
  • 添加厂商依赖
  • 配置 AndroidManifest.xml
  • 请求 Android 通知权限
  • 配置 iOS 工程
  • 启用推送能力
  • 修改 AppDelegate
  • 实现离线推送
  • 提供平台对应的推送插件
  • 创建 NIM 实例并设置推送配置
  • 前后台状态处理
  • 发送可离线推送的消息
  • 测试离线推送
  • 通知点击处理边界
  • 常见问题