You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

React Native应用关闭时如何触发UI动作?视频会议来电场景求助

作为React Native新手,你遇到的这个问题确实是实时通话类App的核心难点——毕竟应用完全关闭后,常规的前端逻辑根本跑不起来。结合我做视频会议App的踩坑经验,给你两个具体可落地的方案,完全避开抽象理论:

方案1:应用关闭时保持Socket.io连接(仅Android可行,iOS受限)

首先得明确:iOS在应用完全关闭后,除了苹果官方允许的VoIP推送,没有任何合法方式保持长连接,所以这个方案只适用于Android。

Android可以通过后台服务让Socket连接持续运行,具体步骤:

  • 第一步:安装后台服务依赖
    react-native-background-service这个库,直接执行:
    npm install react-native-background-service --save
    
    然后在Android项目的AndroidManifest.xml里添加必要权限和服务注册:
    <!-- 添加权限 -->
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    
    <!-- 在<application>标签内注册服务 -->
    <service android:name="com.asterinet.react.bgservice.BackgroundService" />
    
  • 第二步:编写后台Socket连接逻辑
    在你的App入口文件(比如App.js)里添加以下代码,启动后台服务并初始化Socket:
    import BackgroundService from 'react-native-background-service';
    import io from 'socket.io-client';
    import RNCallKeep from 'react-native-callkeep';
    
    // 配置后台服务的前台通知(Android要求后台服务必须有前台通知,避免被系统杀死)
    BackgroundService.configure({
      taskName: 'CallSocketService',
      taskTitle: '监听来电',
      taskDesc: '视频会议服务运行中',
      taskIcon: { name: 'ic_launcher', type: 'mipmap' },
      color: '#4CAF50',
    });
    
    // 后台服务启动时初始化Socket
    BackgroundService.on('start', () => {
      const socket = io('你的Socket服务器地址', {
        transports: ['websocket'], // 强制用websocket,避免轮询被系统限制
        reconnection: true,
        reconnectionDelay: 1000,
        reconnectionAttempts: Infinity,
      });
    
      // 监听来电事件
      socket.on('incoming-call', (callData) => {
        // 直接触发CallKeep显示来电界面
        RNCallKeep.displayIncomingCall(
          callData.callId,
          callData.callerName,
          callData.callerName,
          'video',
          false
        );
      });
    
      // 把服务设为前台状态,防止被系统回收
      BackgroundService.setForeground(true);
    });
    
    // 启动后台服务
    BackgroundService.start();
    
  • 第三步:处理Android系统限制
    国产ROM(小米、华为、OPPO等)会主动杀死后台服务,所以要引导用户开启:
    • 自启动权限
    • 忽略电池优化权限
      可以用react-native-permissions库请求忽略电池优化权限:
    import Permissions from 'react-native-permissions';
    
    async function requestBatteryOptimization() {
      const status = await Permissions.request('android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS');
      if (status === 'granted') {
        console.log('已获得忽略电池优化权限');
      }
    }
    
方案2:无需点击推送直接唤醒应用(跨平台通用,推荐)

这个方案是WhatsApp这类App的标配,iOS用VoIP推送,Android用FCM数据消息,不需要用户点击通知就能直接唤醒应用并显示来电。

iOS:VoIP推送(唯一合法的后台唤醒方式)

iOS的普通FCM推送必须点击才能打开应用,但VoIP推送是苹果专门为通话类App开放的通道,即使应用完全关闭也能直接唤醒:

  • 第一步:准备开发者配置
    在苹果开发者后台开启VoIP服务,生成对应的APNs证书(和普通推送证书分开)。
  • 第二步:安装VoIP推送库
    react-native-voip-push-notification
    npm install react-native-voip-push-notification --save
    
  • 第三步:配置iOS项目
    AppDelegate.m里添加VoIP推送的回调(直接复制粘贴即可,不用懂OC):
    #import <RNVoipPushNotification.h>
    
    // 添加以下方法到AppDelegate类中
    - (void)pushRegistry:(PKPushRegistry *)registry didUpdatePushCredentials:(PKPushCredentials *)credentials forType:(PKPushType)type {
      if ([type isEqualToString:PKPushTypeVoIP]) {
        [RNVoipPushNotification didUpdatePushCredentials:credentials forType:(NSString *)type];
      }
    }
    
    - (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(PKPushType)type withCompletionHandler:(void (^)(void))completion {
      if ([type isEqualToString:PKPushTypeVoIP]) {
        [RNVoipPushNotification didReceiveIncomingPushWithPayload:payload forType:(NSString *)type];
        completion();
      }
    }
    
  • 第四步:处理VoIP推送唤醒逻辑
    App.js里添加代码,收到推送直接触发CallKeep:
    import VoipPushNotification from 'react-native-voip-push-notification';
    import RNCallKeep from 'react-native-callkeep';
    
    // 注册VoIP推送Token,发给你的服务器用来推送来电
    VoipPushNotification.registerVoipToken((token) => {
      console.log('VoIP Token:', token);
      // 这里把token上传到你的服务器
    });
    
    // 监听VoIP推送事件
    VoipPushNotification.addEventListener('notification', (notification) => {
      const callData = notification.data;
      // 直接显示来电界面
      RNCallKeep.displayIncomingCall(
        callData.callId,
        callData.callerName,
        callData.callerName,
        'video',
        false
      );
    });
    
    注意:VoIP推送只能用来推送来电相关内容,不能用来发广告或其他消息,否则会被苹果下架。

Android:FCM数据消息(无需点击唤醒)

Android的FCM分两种消息:通知消息(需要用户点击)和数据消息(直接唤醒后台逻辑),我们用数据消息:

  • 第一步:安装Firebase Messaging库
    npm install @react-native-firebase/app @react-native-firebase/messaging --save
    
  • 第二步:配置后台消息处理服务
    在Android项目的AndroidManifest.xml里添加自定义消息服务:
    <service
      android:name=".MyFirebaseMessagingService"
      android:exported="false">
      <intent-filter>
        <action android:name="com.google.firebase.MESSAGING_EVENT" />
      </intent-filter>
    </service>
    
    然后在android/app/src/main/java/com/你的包名/下创建MyFirebaseMessagingService.java文件(复制粘贴即可):
    package com.你的包名;
    
    import com.google.firebase.messaging.FirebaseMessagingService;
    import com.google.firebase.messaging.RemoteMessage;
    import io.invertase.firebase.messaging.RNFirebaseMessagingService;
    
    public class MyFirebaseMessagingService extends RNFirebaseMessagingService {
      @Override
      public void onMessageReceived(RemoteMessage remoteMessage) {
        super.onMessageReceived(remoteMessage);
      }
    }
    
  • 第三步:编写后台消息处理逻辑
    App.js里用Headless JS处理后台消息:
    import messaging from '@react-native-firebase/messaging';
    import RNCallKeep from 'react-native-callkeep';
    
    // 后台消息处理器
    async function handleBackgroundMessage(message) {
      // 判断是不是来电消息
      if (message.data.type === 'incoming-call') {
        const callData = message.data;
        // 触发CallKeep显示来电
        RNCallKeep.displayIncomingCall(
          callData.callId,
          callData.callerName,
          callData.callerName,
          'video',
          false
        );
      }
    }
    
    // 注册后台消息处理器
    messaging().setBackgroundMessageHandler(handleBackgroundMessage);
    
    注意:数据消息不会自动显示通知栏,如果需要显示,可以在处理逻辑里添加本地通知:
    import notifee from '@notifee/react-native';
    
    async function handleBackgroundMessage(message) {
      if (message.data.type === 'incoming-call') {
        // 显示本地通知(可选)
        await notifee.displayNotification({
          title: '来电',
          body: `${message.data.callerName}邀请你视频通话`,
          android: {
            channelId: 'call-channel',
            priority: 'high',
          },
        });
        // 触发CallKeep
        RNCallKeep.displayIncomingCall(...);
      }
    }
    
最后补充几个关键注意事项
  • CallKeep的基础配置:不管用哪个方案,都要正确配置CallKeep的权限,比如iOS的NSVoIPUsageDescriptionNSCameraUsageDescription,Android的READ_PHONE_STATECALL_PHONE等权限。
  • iOS的VoIP推送必须经过苹果审核,不能滥用;Android的后台逻辑可能会被系统限制,要引导用户开启必要权限。
  • 测试的时候,一定要测试完全关闭应用的场景,而不是后台挂起,两者的行为差异很大。

内容的提问来源于stack exchange,提问作者Rakib Uddin

火山引擎 最新活动