Если вы используете FCM API для создания запросов на отправку программным способом, то со временем можете обнаружить, что тратите ресурсы впустую, отправляя сообщения на неактивные устройства с устаревшими регистрациями. Это может повлиять на данные о доставке писем, которые показываются в консоли Firebase или экспортируются в BigQuery, и привести к резкому (но недействительному) снижению показателей доставки. В этом руководстве рассказывается о том, как обеспечить эффективный таргетинг сообщений и достоверную отчетность о доставке.
Устаревшие и просроченные регистрации
Устаревшие регистрации связаны с неактивными устройствами, которые не подключались к FCM более месяца. Со временем вероятность того, что устройство снова подключится к FCM, будет снижаться. Отправка сообщений и разветвление тем для таких неактивных регистраций вряд ли будут выполнены.
Регистрация может устареть по нескольким причинам. Например, устройство, с которым связана регистрация, может быть потеряно, уничтожено или забыто в хранилище.
Если регистрация на устройстве Android неактивна в течение 270 дней, FCM считает ее устаревшей и удаляет. Когда срок регистрации истекает, FCM помечает ее как недействительную и отклоняет отправку на нее. Обратите внимание, что идентификаторы установки Firebase (FID) управляются сервисом установки Firebase (FIS), а не FCM. Если устройство снова подключится и приложение будет открыто после того, как регистрация устройства будет удалена сборщиком мусора, клиентское приложение снова зарегистрируется в FCM, используя идентификатор экземпляра приложения, полученный от FIS. Обратите внимание, что FID может измениться. Подробнее о том, когда FID перевыпускаются, рассказывается в статье Управление установками Firebase.
На других платформах, например iOS, FCM использует базовый сервис push-уведомлений (например, APNs), у которого нет такого же срока действия на основе неактивности (270 дней). Мы рекомендуем поддерживать актуальность регистраций и удалять устаревшие.
Основные рекомендации
При создании запросов на отправку с помощью API FCM в любом приложении необходимо соблюдать определенные основные правила. Основные рекомендации:
- Получите идентификаторы установки Firebase (FID) из FCM и сохраните их на сервере приложения. Важная роль сервера – отслеживать зарегистрированные идентификаторы FID каждого клиента и поддерживать актуальный список активных идентификаторов FID. Мы настоятельно рекомендуем добавить в базу данных временную метку регистрации и обновлять ее при каждой загрузке данных о регистрации.
- Поддерживайте актуальность регистрации и удаляйте устаревшие записи. Помимо удаления регистраций, которые FCM больше не считает действительными, вы можете отслеживать другие признаки того, что регистрации устарели, и удалять их заранее. В этом руководстве рассказывается о том, как это сделать.
Как получать и хранить идентификаторы установки Firebase
При первом запуске приложения FCM SDK регистрирует экземпляр приложения в FCM и возвращает идентификатор установки Firebase (FID). Этот идентификатор необходимо включать в запросы на отправку целевых уведомлений через API или использовать для подписки на темы.
Мы настоятельно рекомендуем сохранять FID на сервере приложения вместе с временной меткой при каждой загрузке. Обновляя временную метку при каждом запросе на загрузку, ваш сервер узнает, когда экземпляр приложения был открыт в последний раз и успешно синхронизирован с внутренним сервером FCM.
В зависимости от того, включена или отключена автоматическая инициализация (в том числе если она не поддерживается), регистрацию и обновления следует выполнять следующим образом:
- (Рекомендуется) Если включена автоматическая инициализация, SDK автоматически обновляет регистрацию и отслеживает изменения. Обратный вызов
onRegistered()регулярно вызывается при синхронизации во время запуска приложения, а также при изменении FID. Просто реализуйте этот обратный вызов, чтобы загрузить идентификатор FID на свой сервер и сохранить текущую временную метку. - Если автоинициализация отключена, обратный вызов
onRegistered()не будет автоматически вызываться при запуске. Чтобы отслеживать регистрации и поддерживать их актуальность, вызывайтеregister()при запуске приложения, например в Android – в методеonCreate()основного действия. При успешном звонке запускается процесс регистрации FCM с использованием FID, который передается в обратный вызовonRegistered(). Это позволяет приложению загрузить FID и обновить временную метку на сервере.
Пример: хранение идентификаторов и временных меток в Cloud Firestore
Например, вы можете использовать Cloud Firestore, чтобы хранить идентификаторы экземпляров в коллекции 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 идентификатора установки Firebase (рекомендуется)
Если клиентское приложение использует API FID, вам не нужно планировать периодические фоновые задачи в клиентском приложении для получения или обновления регистраций. При автоматической инициализации SDK автоматически обновляет FID и регулярно передает его в обратный вызов onRegistered() при запуске приложения.
Чтобы сервер был актуальным, реализуйте стратегии загрузки при запуске, описанные в разделе Как получать и хранить идентификаторы установки Firebase:
- Автоинициализация включена. SDK автоматически обеспечивает отправку на ваш сервер последнего FID при регулярной синхронизации во время запуска приложения.
- Автоматическая инициализация отключена или не поддерживается. Вызовите
register()при запуске приложения (например, в Android в методеonCreate()основного действия), чтобы принудительно выполнить последовательность регистрации и активировать передачу 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(); });
});
Как отменить подписку на темы для устаревших регистраций
Если вы используете темы, то можете отменить подписку устаревших регистраций на темы, на которые они подписаны. Для этого нужно выполнить два действия:
- Приложение должно повторно подписываться на темы при каждом изменении идентификатора установки Firebase (FID). Это позволяет автоматически восстанавливать подписки, когда приложение снова становится активным.
- Если экземпляр приложения неактивен в течение месяца (или другого заданного вами периода), отмените его подписку на темы с помощью Firebase Admin SDK, чтобы удалить сопоставление идентификатора установки Firebase с темой из серверной части FCM.
Благодаря этим двум шагам рассылка будет выполняться быстрее, поскольку в ней будет меньше устаревших регистраций, а устаревшие экземпляры приложения будут автоматически повторно подписываться, когда снова станут активными.
Как отслеживать успешность доставки
Чтобы получать наиболее точные данные о доставке сообщений, отправляйте их только в активные экземпляры приложения. Это особенно важно, если вы регулярно отправляете сообщения в темы с большим количеством подписчиков. Если часть этих подписчиков неактивна, со временем это может существенно повлиять на статистику доставки.
Прежде чем настраивать таргетинг сообщений на экземпляр приложения, учитывайте следующее:
- Указывают ли Google Аналитика, данные, собранные в BigQuery, или другие сигналы отслеживания на то, что регистрация активна?
- Были ли предыдущие попытки доставки неудачными в течение определенного периода времени?
- Обновлялся ли идентификатор установки Firebase на ваших серверах за последний месяц?
- Для устройств Android FCM Data API сообщает о высоком проценте сбоев доставки сообщений из-за
droppedDeviceInactive?
Подробнее о доставке сообщений…