Передовые методы управления регистрацией FCM

Если вы используете API FCM для программного формирования запросов на отправку, со временем вы можете обнаружить, что тратите ресурсы впустую, отправляя сообщения на неактивные устройства с устаревшими регистрациями. Эта ситуация может повлиять на данные о доставке сообщений, отображаемые в консоли Firebase или экспортируемые в BigQuery , проявляясь в виде резкого (но фактически некорректного) падения показателей доставки. В этом руководстве рассматриваются некоторые меры, которые вы можете предпринять для обеспечения эффективного таргетирования сообщений и корректной отчетности о доставке.

Устаревшие и просроченные регистрации

Устаревшие регистрации связаны с неактивными устройствами, которые не подключались к FCM более месяца. Со временем вероятность повторного подключения устройства к FCM снижается. Отправка сообщений и рассылка тем для таких устаревших регистраций, скорее всего, никогда не будут доставлены.

Существует несколько причин, по которым регистрация может устареть. Например, устройство, к которому привязана регистрация, может быть утеряно, уничтожено или помещено на хранение и забыто.

Для Android, если регистрация неактивна в течение 270 дней, FCM считает её просроченной и удаляет её из системы. После истечения срока действия регистрации FCM помечает её как недействительную и отклоняет отправленные на неё запросы. Обратите внимание, что идентификаторы установок Firebase (FID) управляются службой установок Firebase (FIS), а не FCM. В редких случаях, когда устройство подключается снова и приложение открывается после удаления его регистрации из системы, клиентское приложение регистрируется повторно в FCM , используя FID, полученный из FIS. Обратите внимание, что FID может измениться; подробности о перевыпуске FID см. в разделе «Управление установками Firebase» .

Для других платформ, таких как iOS, FCM использует базовую службу push-уведомлений (например, APN), которая не имеет такого же 270-дневного срока действия, зависящего от неактивности. Мы рекомендуем вам заблаговременно поддерживать актуальность регистраций и удалять устаревшие регистрации .

Основные передовые методы

В любом приложении, использующем API FCM для программного формирования запросов на отправку, следует придерживаться ряда основных принципов. Основные рекомендации следующие:

  • Получите идентификаторы установки Firebase (FID) из FCM и сохраните их на сервере приложений. Важная роль сервера — отслеживать зарегистрированные FID каждого клиента и поддерживать актуальный список активных FID. Мы настоятельно рекомендуем внедрить метку времени регистрации в вашу базу данных и обновлять ее при каждой загрузке регистрации.
  • Поддерживайте актуальность регистрационных данных и удаляйте устаревшие регистрации. Помимо удаления регистраций, которые FCM больше не считает действительными, вы можете отслеживать другие признаки устаревания регистраций и удалять их заблаговременно. В этом руководстве описаны некоторые из вариантов достижения этой цели.

Получение и сохранение идентификаторов установки Firebase.

При первом запуске вашего приложения SDK FCM регистрирует экземпляр приложения в FCM и возвращает идентификатор установки Firebase (FID). Этот идентификатор необходимо указывать в целевых запросах на отправку данных из API или использовать для подписки на темы.

Мы настоятельно рекомендуем сохранять FID на сервере приложений вместе с меткой времени при каждой загрузке. Обновляя метку времени при каждом запросе на загрузку, ваш сервер будет знать, когда экземпляр приложения был в последний раз открыт и успешно синхронизирован с бэкэндом FCM .

В зависимости от того, включена или отключена (включая режим «не поддерживается») автоматическая инициализация, регистрацию и обновления следует обрабатывать следующим образом:

  • (Рекомендуется) При включенной автоматической инициализации: SDK автоматически поддерживает актуальность регистрации и отслеживает изменения. Функция обратного вызова onRegistered() регулярно вызывается при рутинной синхронизации во время запуска приложения, а также при изменении FID. Просто реализуйте эту функцию обратного вызова, чтобы загрузить FID на ваш сервер и сохранить текущую метку времени.
  • Если автоматическая инициализация отключена: коллбэк onRegistered() не будет автоматически вызываться при запуске. Чтобы отслеживать регистрации и поддерживать их актуальность, вызывайте register() при запуске приложения; например, на Android — в методе onCreate() основной активности. Успешный вызов запускает процесс регистрации FCM с использованием FID и передает его в коллбэк onRegistered() , позволяя вашему приложению загрузить FID и обновить метку времени на вашем сервере.

Пример: хранение FID и временных меток в Cloud Firestore

Например, вы можете использовать Cloud Firestore для хранения FID в коллекции под названием fcmRegistrations . Каждый идентификатор документа в коллекции соответствует идентификатору пользователя, а документ хранит текущий FID и метку времени последнего обновления. Используйте функцию set , как показано в этом примере на Kotlin:

private fun sendRegistrationToServer(installationId: String?) {
    // If you're running your own server, call API to send registration details and today's date for the user

    // Example shown uses Firestore
    // Add FID and timestamp to Firestore for this user
    val deviceFid = hashMapOf(
        "installationId" to installationId,
        "timestamp" to FieldValue.serverTimestamp(),
    )
    // Get user ID from Firebase Auth or your own server
    Firebase.firestore.collection("fcmRegistrations").document("myuserid")
        .set(deviceFid)
}

При каждой успешной регистрации или обновлении идентификатора установки Firebase вызывается функция обратного вызова onRegistered() . Вам следует реализовать эту функцию обратного вызова для загрузки идентификатора установки и обновления метки времени:

override fun onRegistered(installationId: String) {
    Log.d(TAG, "Registered installation ID: $installationId")

    // Send the Firebase Installation ID (FID) to your app server. Your app
    // server should save the FID and update the timestamp upon receipt.
    sendRegistrationToServer(installationId)
}

В случаях, когда автоматическая инициализация отключена, вызовите register() при запуске приложения (например, в onCreate() ), чтобы запустить процесс регистрации и доставку FID через onRegistered() :

// Trigger manual registration if auto-initialization is turned off.
FirebaseMessaging.getInstance().register()
    .addOnCompleteListener(this) { task ->
        if (task.isSuccessful) {
            // The registration callback onRegistered() will be invoked with the current FID.
        } else {
            Log.w(TAG, "Failed to register with Firebase Cloud Messaging", task.exception)
        }
    }

Поддерживайте актуальность регистрационных данных и удаляйте устаревшие записи.

Определить, является ли регистрация новой или устаревшей, не всегда просто. Чтобы охватить все случаи, следует установить пороговое значение, при котором регистрация считается устаревшей. По умолчанию FCM считает регистрацию устаревшей, если экземпляр приложения не подключался в течение месяца. Любая регистрация старше одного месяца, скорее всего, относится к неактивному устройству; активное устройство в противном случае обновило бы свою регистрацию.

В зависимости от ваших конкретных задач, одного месяца может быть слишком мало или слишком много, поэтому вам предстоит определить критерии, которые подходят именно вам.

Выявление некорректных ответов от бэкэнда FCM

Обязательно выявляйте недействительные ответы от FCM и удаляйте из системы все заведомо недействительные или просроченные регистрации. При использовании API HTTP v1 эти сообщения об ошибках могут указывать на то, что ваш запрос был направлен на недействительные или просроченные регистрации:

  • UNREGISTERED (HTTP 404)
  • INVALID_ARGUMENT (HTTP 400)

Если вы уверены в корректности содержимого сообщения и получили один из этих ответов для целевой регистрации, можно безопасно удалить запись об этой регистрации, поскольку она больше никогда не будет считаться действительной. Например, для удаления недействительных регистраций из Cloud Firestore можно развернуть и запустить функцию, подобную следующей:

        // Firebase Installation ID comes from the client FCM SDKs
        const firebaseInstallationId = 'YOUR_FIREBASE_INSTALLATION_ID';

        const message = {
            data: {
                // Information you want to send inside of notification
            },
            fid: firebaseInstallationId
        };

        // Send message to device with provided Firebase Installation ID
        getMessaging().send(message)
        .then((response) => {
            // Response is a message ID string.
        })
        .catch((error) => {
            // Delete registration for user if error code is UNREGISTERED or INVALID_ARGUMENT.
            if (error.errorCode == "messaging/registration-token-not-registered") {
                // If you're running your own server, call API to delete the registration for the user
                // Example shown uses Firestore
                // Get user ID from Firebase Auth or your own server
                Firebase.firestore.collection("fcmRegistrations").document(user.uid).delete()
            }
        });

FCM возвращает недействительный ответ, если регистрация для устройства Android истекла после 270 дней бездействия или если клиент явно отменил регистрацию. Если вам необходимо более точно отслеживать устаревание в соответствии с вашими собственными определениями, вы можете заблаговременно удалять устаревшие регистрации .

Регулярно обновляйте данные о регистрации.

Независимо от того, основаны ли ваши регистрации на FID или устаревших регистрационных токенах, ваш сервер должен всегда обновлять метку времени регистрации в базе данных при каждом запросе на загрузку. Эта метка времени служит сигналом для установки приложения, сообщая клиенту об успешном открытии приложения и синхронизации с бэкэндом FCM . В зависимости от используемых API, реализуйте соответствующую стратегию:

Для клиентских приложений, использующих API FID, вам не нужно планировать периодические фоновые задачи в вашем клиентском приложении для получения или обновления регистраций. SDK автоматически позаботится об обновлении в рамках автоинициализации, регулярно передавая правильный текущий FID в ваш коллбэк onRegistered() при регулярной синхронизации во время запуска приложения.

Для поддержания сервера в актуальном состоянии используйте стратегии загрузки при запуске, описанные в разделе «Получение и сохранение идентификаторов установки Firebase» :

  • Автоматическая инициализация включена: SDK автоматически гарантирует отправку последнего FID на ваш сервер при регулярной синхронизации во время запуска приложения.
  • Автоматическая инициализация отключена или не поддерживается: вызовите register() при запуске приложения (например, на Android, в методе onCreate() основного действия), чтобы принудительно запустить последовательность регистрации и инициировать передачу FID в ваш коллбэк onRegistered() .

Эти стратегии гарантируют, что ваш сервер всегда будет иметь самый актуальный активный FID и сможет автоматически восстанавливаться после неудачных загрузок, что делает приложение очень отказоустойчивым.

Устаревшие API для регистрации токенов

Если вы используете устаревшие регистрационные токены, клиентский SDK не выполняет автоматическое обновление при регулярных синхронизациях. Поэтому мы рекомендуем периодически получать и обновлять все регистрационные токены на вашем сервере. Для этого вам потребуется:

  • Добавьте в клиентское приложение логику для получения текущего токена с помощью соответствующего вызова API (например, token(completion): для платформ Apple или getToken() для Android), а затем отправьте текущий токен на сервер приложения для хранения (с меткой времени). Это может быть ежемесячная задача, настроенная для обработки всех клиентов или токенов.
  • Добавьте серверную логику для регулярного обновления метки времени токена, независимо от того, изменился ли сам токен или нет.

Пример логики Android для обновления устаревших токенов с помощью WorkManager можно найти в статье «Управление токенами облачных сообщений» в блоге Firebase.

Независимо от выбранного вами графика обновления, обязательно обновляйте токены периодически. Обновление раз в месяц обеспечивает хороший баланс между расходом заряда батареи и обнаружением неактивных регистрационных токенов. Благодаря этому обновлению вы также гарантируете, что любое устройство, перешедшее в неактивное состояние, обновит свою регистрацию, когда снова станет активным. Нет смысла обновлять токены чаще, чем раз в неделю.

Удалить устаревшие регистрации

Перед отправкой сообщений на устройство убедитесь, что метка времени регистрации устройства находится в пределах заданного периода ожидания. Например, вы можете использовать Cloud Functions for Firebase для ежедневной проверки, чтобы убедиться, что метка времени находится в пределах определенного периода ожидания, например, const EXPIRATION_TIME = 1000 * 60 * 60 * 24 * 30; а затем удалить устаревшие регистрации:

exports.pruneRegistrations = functions.pubsub.schedule('every 24 hours').onRun(async (context) => {
  // Get all documents where the timestamp exceeds is not within the past month
  const staleRegistrationsResult = await admin.firestore().collection('fcmRegistrations')
      .where("timestamp", "<", Date.now() - EXPIRATION_TIME)
      .get();
  // Delete devices with stale registrations
  staleRegistrationsResult.forEach(function(doc) { doc.ref.delete(); });
});
exports.pruneTokens = functions.pubsub.schedule('каждые 24 часа').onRun(async (context) => { // Получить все документы, у которых метка времени превышает определенный период, но не относится к прошлому месяцу const staleTokensResult = await admin.firestore().collection('fcmTokens') .where("timestamp", "<", Date.now() - EXPIRATION_TIME) .get(); // Удалить устройства с устаревшими токенами staleTokensResult.forEach(function(doc) { doc.ref.delete(); }); });

Отписаться от устаревших регистраций по темам

Если вы используете темы, вам также может потребоваться отписаться от устаревших регистраций в темах, на которые они подписаны. Это включает в себя два шага:

  1. Ваше приложение должно автоматически подписываться на темы всякий раз, когда изменяется идентификатор установки Firebase (FID). Это позволит подпискам автоматически возобновить работу, когда приложение снова станет активным.
  2. Если экземпляр приложения простаивает в течение месяца (или вашего собственного периода неактивности), вам следует отменить его подписку на темы, используя Firebase Admin SDK , чтобы удалить сопоставление идентификатора установки Firebase с темой из бэкэнда FCM .

Преимущество этих двух шагов заключается в том, что рассылка сообщений будет происходить быстрее, поскольку будет меньше устаревших регистраций, а ваши устаревшие экземпляры приложения автоматически подпишутся заново, как только снова станут активными.

Оцените успешность доставки.

Для получения наиболее точной картины доставки сообщений лучше всего отправлять сообщения только активно используемым экземплярам приложения. Это особенно важно, если вы регулярно отправляете сообщения в темы с большим количеством подписчиков; если часть этих подписчиков на самом деле неактивна, это может существенно повлиять на статистику доставки в долгосрочной перспективе.

Прежде чем направлять сообщения конкретному экземпляру приложения, следует учесть следующее:

  • Указывают ли данные Google Analytics, данные, полученные в BigQuery, или другие сигналы отслеживания на то, что регистрация активна?
  • Предыдущие попытки доставки неизменно терпели неудачу в течение определенного периода времени?
  • Обновлялся ли идентификатор установки Firebase на ваших серверах за последний месяц?
  • Сообщает ли API данных FCM о высоком проценте сбоев доставки сообщений на устройствах Android из-за droppedDeviceInactive ?

Для получения дополнительной информации о доставке см. раздел «Понимание доставки сообщений» .