Рекомендации по управлению регистрацией в 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-уведомлений (например, APNs), в котором нет такого же 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() основного объекта Activity. Успешный вызов запускает процесс регистрации FCM с использованием FID и доставляет его в обратный вызов onRegistered(), позволяя приложению загрузить FID и обновить временную метку на сервере.

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

Например, вы можете использовать Cloud Firestore для хранения идентификаторов установок Firebase в коллекции 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(). Чтобы загрузить FID и обновить временную метку, необходимо реализовать этот обратный вызов:

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 и удаляйте из системы все недействительные или устаревшие регистрации. При использовании HTTP v1 API эти сообщения об ошибках могут указывать на то, что ваш запрос на отправку был нацелен на недействительные или просроченные регистрации:

  • 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, регулярно передавая правильный текущий FID в обратный вызов onRegistered() при обычной синхронизации во время запуска приложения.

Чтобы на сервере всегда были актуальные данные, реализуйте стратегии загрузки при запуске, описанные в разделе Получение и хранение идентификаторов установок Firebase.

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

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

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

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

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

Пример логики Android для обновления устаревших токенов с помощью WorkManager приведен в статье Управление токенами Cloud Messaging в блоге 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('every 24 hours').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 Аналитики, BigQuery или других систем отслеживания на то, что регистрация активна?
  • Не удавалось ли выполнить доставку в течение определенного периода времени?
  • Обновлялся ли идентификатор установки Firebase на ваших серверах за последний месяц?
  • Сообщает ли FCM Data API о высоком проценте сбоев доставки сообщений на устройства Android из-за droppedDeviceInactive?

Подробнее о доставке сообщений…