React Native后台可以同步数据吗?

问题描述 投票:0回答:1

几周以来我一直在努力解决应用程序中的功能。

我会解释我的项目;我有一个反应本机应用程序,可以连接多个用户。用户可以创建事件(例如会议),而其他用户会收到通知。该事件被添加到创建会议的用户(我们称他为用户 1)的本机日历中,我也想将其添加到受邀参加会议的用户(我们称他为用户 2)中。当用户打开应用程序或应用程序状态更改时,我为用户 2 同步本机日历中的事件的功能工作正常。但我想同步用户 2 的本机日历,即使他没有打开应用程序,就像在后台一样。

为此,我想到了两个解决方案。最好的就是通知。因此,当用户 1 创建会议时,用户 2 总是会收到通知,我的想法是当显示通知时,我可以同步日历。因此,对于通知,我使用 firebase,通知显示在后台,但我的代码没有执行。问题是我的代码运行后台消息时出现一些错误。我的错误表明我使用 id ReactNativeFirebaseMessagingHeadlessTask 运行多个 HeadlessTask。我不知道为什么会收到此错误,但我仍然有后台通知,但没有 console.log()。所以我通过通知放弃了这个想法。

这是我注册后台通知的代码:

import * as React from 'react';
import { useState, useEffect } from 'react';

import { API_KEY, PROJECT_ID, STORAGE_BUCKET, MESSAGING_SENDER_ID, APP_ID } from '@env';

import firebase from '@react-native-firebase/app';
import messaging from '@react-native-firebase/messaging';
import { AppRegistry } from 'react-native';

import { syncCalendarEventId } from '../utils/calendarSync';

const register = async () => {
    const firebaseConfig = {
      apiKey: `${API_KEY}`,
      projectId: `${PROJECT_ID}`,
      storageBucket: `${STORAGE_BUCKET}`,
      messagingSenderId: `${MESSAGING_SENDER_ID}`,
      appId: `${APP_ID}`,
      // databaseURL: '...',
      // authDomain: '...',
    };

    firebase
    .initializeApp(firebaseConfig)
    .then(() => {
        registerBackgroundMessage();
    })
    .catch(error => {
    });
};

export async function initializeNotifications() {
    await register();

    if (!firebase.apps.length) {
        register();
    } else {
        registerBackgroundMessage();
    }
}

// Request user permission for notifications
export async function requestUserPermission() {
    const authStatus = await messaging().requestPermission();
    const enabled =
        authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
        authStatus === messaging.AuthorizationStatus.PROVISIONAL;

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

// Register background handler
const registerBackgroundMessage = () => {
    console.log("bg")
    if(Platform.OS === 'ios'){
        messaging().setBackgroundMessageHandler(async remoteMessage => {
            console.log('Notification reçue en fond ! : ', remoteMessage);
            if(remoteMessage.data && remoteMessage.data.sync_required == 1){
                syncCalendarEventId();
            }
        });
    } else if(Platform.OS === 'android'){
        const headlessNotificationHandler = async (remoteMessage) => {
            console.log('Notification reçue en fond ! : ', remoteMessage);
            // Ajoutez ici toute logique que vous souhaitez traiter en arrière-plan
        };
        
        // Enregistrez la tâche headless
        AppRegistry.registerHeadlessTask(
            'ReactNativeFirebaseMessagingHeadlessTask', 
            () => headlessNotificationHandler
        );
    }
};

我得到的第二个想法(但我认为最糟糕的)是尝试每 30 分钟或每小时同步一次事件。我尝试使用 react-native-background-fetch 库来做到这一点,但仍然不起作用。我不知道为什么,但我的代码在 20 或 25 分钟后没有运行事件。这是我的这个库的代码:

  useEffect(() => {
    const configureBackgroundFetch = async () => {
      console.log("configure bg");
      BackgroundFetch.configure(
        {
          minimumFetchInterval: 15,
          stopOnTerminate: false,
          startOnBoot: true,
        },
        async (taskId) => {
          console.log("sync")
          syncCalendarEventId();
          console.log(`Tâche de fetch avec ID ${taskId} exécutée`);
          BackgroundFetch.finish(taskId);
        },
        (error) => {
          console.error('Erreur de configuration BackgroundFetch:', error);
        }
      );
      
      console.log("fetch start");
      BackgroundFetch.start();
      console.log("fetch started");
    };

    configureBackgroundFetch();

  }, []);

有关信息,useEffect 位于我的 App.js 文件中。对于后台获取库,我认为我已经正确安装了它。

  • 我在xcode中检查了后台获取后台处理后台通知
  • 我使用以下代码更新了文件 Info.plist :
    <key>UIBackgroundModes</key>
    <array>
        <string>remote-notification</string>
        <string>fetch</string>
        <string>processing</string>
    </array>

所以我的问题是:是否可以在后台创建类似日历同步的功能,或者我只是在用户打开我的应用程序时让同步?

感谢您的宝贵帮助:)

javascript react-native background-process
1个回答
0
投票

让我分解一下可行的方法并提供一些建议:

// NotificationService.js
import messaging from '@react-native-firebase/messaging';
import { AppRegistry, Platform } from 'react-native';
import { syncCalendarEventId } from './calendarSync';

class NotificationService {
  async initialize() {
    // Request permissions
    const authStatus = await messaging().requestPermission();
    const enabled =
      authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
      authStatus === messaging.AuthorizationStatus.PROVISIONAL;

    if (!enabled) {
      console.log('User notification permissions denied');
      return false;
    }

    // Get FCM token
    const token = await messaging().getToken();
    console.log('FCM Token:', token);

    // Register background handler
    if (Platform.OS === 'ios') {
      messaging().setBackgroundMessageHandler(this.handleBackgroundMessage);
    } else {
      // For Android, we only need to register the task once
      if (!AppRegistry.getRunnable('ReactNativeFirebaseMessagingHeadlessTask')) {
        AppRegistry.registerHeadlessTask(
          'ReactNativeFirebaseMessagingHeadlessTask',
          () => this.handleBackgroundMessage
        );
      }
    }

    // Register foreground handler
    messaging().onMessage(this.handleForegroundMessage);

    return true;
  }

  handleBackgroundMessage = async (remoteMessage) => {
    console.log('Background Message received:', remoteMessage);
    
    try {
      if (remoteMessage.data?.sync_required === '1') {
        await syncCalendarEventId();
        console.log('Calendar sync completed in background');
      }
    } catch (error) {
      console.error('Error in background sync:', error);
    }
  }

  handleForegroundMessage = async (remoteMessage) => {
    console.log('Foreground Message received:', remoteMessage);
    
    try {
      if (remoteMessage.data?.sync_required === '1') {
        await syncCalendarEventId();
        console.log('Calendar sync completed in foreground');
      }
    } catch (error) {
      console.error('Error in foreground sync:', error);
    }
  }
}

export default new NotificationService();

// App.js
import React, { useEffect } from 'react';
import BackgroundFetch from 'react-native-background-fetch';
import NotificationService from './NotificationService';

const App = () => {
  useEffect(() => {
    const setupBackgroundTasks = async () => {
      // Initialize notifications
      await NotificationService.initialize();

      // Configure background fetch as a fallback
      BackgroundFetch.configure({
        minimumFetchInterval: 15, // 15 minutes minimum
        stopOnTerminate: false,
        startOnBoot: true,
        enableHeadless: true,
        forceAlarmManager: true, // Use AlarmManager for more reliable scheduling on Android
      }, async (taskId) => {
        console.log('[BackgroundFetch] Task received:', taskId);
        try {
          await syncCalendarEventId();
          console.log('[BackgroundFetch] Calendar sync completed');
        } catch (error) {
          console.error('[BackgroundFetch] Sync error:', error);
        }
        BackgroundFetch.finish(taskId);
      }, (error) => {
        console.error('[BackgroundFetch] Configure error:', error);
      });

      // Start background fetch
      const status = await BackgroundFetch.status();
      console.log('[BackgroundFetch] status:', status);
      
      if (status !== BackgroundFetch.STATUS_RESTRICTED) {
        await BackgroundFetch.start();
      }
    };

    setupBackgroundTasks();

    // Cleanup
    return () => {
      BackgroundFetch.stop();
    };
  }, []);

  return (
    // Your app components
  );
};

export default App;

以下是我对您的情况的分析和建议:

Firebase 云消息传递 (FCM) 方法

  • 这确实是实时同步的最佳方法
  • 发生有关多次 HeadlessTask 注册的错误,因为注册可能会发生多次
  • 在提供的解决方案中,我已将通知逻辑分离到服务类中并确保单一注册
  • 后台处理程序的结构适合 iOS 和 Android

后台获取方法

  • 这应该用作后备机制,而不是主要同步方法
  • iOS和Android对后台任务频率有严格限制
  • 提供的解决方案包括更好的错误处理和日志记录
  • 添加了
    forceAlarmManager: true
    以实现更可靠的 Android 调度

最佳实践实施

  • 结合使用两种方法:

    • FCM 作为立即同步的主要方法
    • 后台获取作为后备以捕获任何错过的更新
  • 正确的错误处理和日志记录以进行调试

  • 将关注点分离到不同的文件中以便更好地维护

针对您的具体案例的建议

  • 实现提供的NotificationService类
  • 将后台获取间隔保持在至少 15 分钟以尊重电池寿命
  • 在你的syncCalendarEventId函数中添加正确的错误处理
  • 考虑为失败的同步添加队列系统
  • 添加时间戳检查以避免重复的日历条目
© www.soinside.com 2019 - 2024. All rights reserved.