Если вы используете API FCM для программного формирования запросов на отправку, со временем вы можете обнаружить, что тратите ресурсы впустую, отправляя сообщения на неактивные устройства с устаревшими регистрационными токенами. Эта ситуация может повлиять на данные о доставке сообщений, отображаемые в консоли Firebase или экспортируемые в BigQuery, проявляясь в виде резкого (но фактически некорректного) снижения показателей доставки. В этом руководстве обсуждаются некоторые меры, которые вы можете предпринять для обеспечения эффективного таргетирования сообщений и корректной отчетности о доставке.
Устаревшие и просроченные регистрационные токены
Устаревшие регистрационные токены — это токены, связанные с неактивными устройствами, которые не подключались к FCM более месяца. Со временем вероятность повторного подключения устройства к FCM снижается. Отправка сообщений и распространение тем по этим устаревшим токенам, скорее всего, никогда не будут доставлены.
Существует несколько причин, по которым токен может устареть. Например, устройство, к которому привязан токен, может быть утеряно, уничтожено или помещено на хранение и забыто.
В Android, когда срок действия устаревших токенов достигает 270 дней бездействия, FCM считает их просроченными . После истечения срока действия токена FCM помечает его как недействительный и отклоняет отправленные на него запросы. Однако, в редких случаях, когда устройство снова подключается и приложение открывается, FCM выдает новый токен для экземпляра приложения.
Для других платформ, таких как iOS, FCM использует базовую службу push-уведомлений (например, APNs), которая не имеет такого же 270-дневного срока действия токена, зависящего от неактивности. Поэтому мы рекомендуем вам заблаговременно поддерживать актуальность токенов и удалять устаревшие регистрационные токены .
Основные передовые методы
В любом приложении, использующем API FCM для программного формирования запросов на отправку, следует придерживаться ряда основных принципов. Основные рекомендации следующие:
- Получайте регистрационные токены из FCM и сохраняйте их на своем сервере. Важная роль сервера — отслеживать токены каждого клиента и поддерживать актуальный список активных токенов. Мы настоятельно рекомендуем внедрить метку времени токена в ваш код и на ваши серверы и регулярно обновлять эту метку времени.
- Поддерживайте актуальность токенов и удаляйте устаревшие. Помимо удаления токенов, которые FCM больше не считает действительными, вы можете отслеживать другие признаки устаревания токенов и удалять их заблаговременно. В этом руководстве описаны некоторые из вариантов достижения этой цели.
Получение и сохранение регистрационных токенов.
При первом запуске вашего приложения SDK FCM генерирует регистрационный токен для экземпляра клиентского приложения. Этот токен необходимо включать в целевые запросы на отправку данных из API или добавлять в подписки на темы для целевого выбора тем.
Мы настоятельно рекомендуем вашему приложению получать этот токен при первом запуске и сохранять его на сервере приложения вместе с меткой времени . Эта метка времени должна быть реализована вашим кодом и серверами, поскольку она не предоставляется SDK FCM .
Кроме того, важно сохранять токен на сервере и обновлять метку времени всякий раз, когда она изменяется, например, когда:
- Приложение восстановлено на новом устройстве.
- Пользователь удаляет или переустанавливает приложение.
- Пользователь очищает данные приложения.
- Приложение снова станет активным после истечения срока действия существующего токена FCM
Пример: хранение токенов и временных меток в Cloud Firestore
Например, вы можете использовать Cloud Firestore для хранения токенов в коллекции под названием fcmTokens . Идентификатор каждого документа в коллекции соответствует идентификатору пользователя, а документ хранит текущий регистрационный токен и его метку времени последнего обновления. Используйте функцию set , как показано в этом примере на Kotlin:
/**
* Persist token to third-party servers.
*
* Modify this method to associate the user's FCM registration token with any server-side account
* maintained by your application.
*
* @param token The new token.
*/
private fun sendTokenToServer(token: String?) {
// If you're running your own server, call API to send token and today's date for the user
// Example shown below with Firestore
// Add token and timestamp to Firestore for this user
val deviceToken = hashMapOf(
"token" to token,
"timestamp" to FieldValue.serverTimestamp(),
)
// Get user ID from Firebase Auth or your own server
Firebase.firestore.collection("fcmTokens").document("myuserid")
.set(deviceToken)
}
При каждом получении токена он сохраняется в Cloud Firestore путем вызова функции sendTokenToServer :
/**
* Called if the FCM registration token is updated. This may occur if the security of
* the previous token had been compromised. Note that this is called when the
* FCM registration token is initially generated so this is where you would retrieve the token.
*/
override fun onNewToken(token: String) {
Log.d(TAG, "Refreshed token: $token")
// If you want to send messages to this application instance or
// manage this apps subscriptions on the server side, send the
// FCM registration token to your app server.
sendTokenToServer(token)
}
var token = Firebase.messaging.token.await()
// Check whether the retrieved token matches the one on your server for this user's device
val preferences = this.getPreferences(Context.MODE_PRIVATE)
val tokenStored = preferences.getString("deviceToken", "")
lifecycleScope.launch {
if (tokenStored == "" || tokenStored != token)
{
// If you have your own server, call API to send the above token and Date() for this user's device
// Example shown below with Firestore
// Add token and timestamp to Firestore for this user
val deviceToken = hashMapOf(
"token" to token,
"timestamp" to FieldValue.serverTimestamp(),
)
// Get user ID from Firebase Auth or your own server
Firebase.firestore.collection("fcmTokens").document("myuserid")
.set(deviceToken).await()
}
}
Поддерживайте актуальность токенов и удаляйте устаревшие токены.
Определить, является ли токен актуальным или устаревшим, не всегда просто. Чтобы охватить все случаи, следует установить пороговое значение, при котором токены считаются устаревшими. По умолчанию FCM считает токен устаревшим, если экземпляр приложения, к которому он относится, не подключался в течение месяца. Любой токен старше одного месяца, скорее всего, является неактивным устройством; активное устройство в противном случае обновило бы свой токен.
В зависимости от ваших конкретных задач, одного месяца может быть слишком мало или слишком много, поэтому вам предстоит определить критерии, которые подходят именно вам.
Обнаружение недействительных ответов токенов от бэкэнда FCM
Обязательно отслеживайте ответы от FCM , содержащие недействительные токены, и удаляйте из системы все заведомо недействительные или просроченные регистрационные токены. В случае с API HTTP v1 эти сообщения об ошибках могут указывать на то, что ваш запрос был направлен на недействительные или просроченные токены:
-
UNREGISTERED(HTTP 404) -
INVALID_ARGUMENT(HTTP 400)
Если вы уверены в действительности содержимого сообщения и получили один из этих ответов для целевого токена, вы можете безопасно удалить запись об этом токене, поскольку он больше никогда не будет действительным. Например, чтобы удалить недействительные токены из Cloud Firestore , вы можете развернуть и запустить функцию, подобную следующей:
// Registration token comes from the client FCM SDKs
const registrationToken = 'YOUR_REGISTRATION_TOKEN';
const message = {
data: {
// Information you want to send inside of notification
},
token: registrationToken
};
// Send message to device with provided registration token
getMessaging().send(message)
.then((response) => {
// Response is a message ID string.
})
.catch((error) => {
// Delete token 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
token for the user
// Example shown below with Firestore
// Get user ID from Firebase Auth or your own server
Firebase.firestore.collection("fcmTokens").document(user.uid).delete()
}
});
FCM возвращает сообщение о недействительном токене, если токен для устройства Android истек после 270 дней бездействия или если клиент явно отменил регистрацию. Если вам необходимо более точно отслеживать устаревшие токены в соответствии с вашими собственными определениями, вы можете заблаговременно удалять устаревшие регистрационные токены .
Регулярно обновляйте токены.
Мы рекомендуем периодически получать и обновлять все регистрационные токены на вашем сервере. Для этого вам потребуется:
- Добавьте в клиентское приложение логику для получения текущего токена с помощью соответствующего вызова API (например,
token(completion):для платформ Apple илиgetToken()для Android), а затем отправьте текущий токен на сервер приложения для хранения (с меткой времени). Это может быть ежемесячная задача, настроенная для обработки всех клиентов или токенов. - Добавьте серверную логику для регулярного обновления метки времени токена, независимо от того, изменился ли сам токен или нет.
Пример логики обновления токенов в Android с использованием WorkManager можно найти в статье «Управление токенами облачных сообщений» в блоге Firebase.
Независимо от выбранного вами графика обновления, обязательно обновляйте токены периодически. Обновление раз в месяц обеспечивает хороший баланс между расходом заряда батареи и обнаружением неактивных регистрационных токенов. Благодаря этому обновлению вы также гарантируете, что любое устройство, перешедшее в неактивное состояние, обновит свою регистрацию, когда снова станет активным. Нет смысла обновлять токены чаще, чем раз в неделю.
Удалить устаревшие регистрационные токены
Перед отправкой сообщений на устройство убедитесь, что метка времени регистрационного токена устройства находится в пределах установленного вами периода недействительности. Например, вы можете использовать Cloud Functions for Firebase для ежедневной проверки того, находится ли метка времени в пределах определенного периода недействительности, например, const EXPIRATION_TIME = 1000 * 60 * 60 * 24 * 30; а затем удалять устаревшие токены:
exports.pruneTokens = functions.pubsub.schedule('every 24 hours').onRun(async (context) => {
// Get all documents where the timestamp exceeds is not within the past month
const staleTokensResult = await admin.firestore().collection('fcmTokens')
.where("timestamp", "<", Date.now() - EXPIRATION_TIME)
.get();
// Delete devices with stale tokens
staleTokensResult.forEach(function(doc) { doc.ref.delete(); });
});
Отписаться от устаревших токенов из тем
Если вы используете темы, вам также может потребоваться отменить регистрацию устаревших токенов в темах, на которые они подписаны. Это включает в себя два шага:
- Ваше приложение должно повторно подписываться на темы раз в месяц и всякий раз, когда изменяется регистрационный токен. Это создает самовосстанавливающееся решение, при котором подписки автоматически возобновляются, когда приложение снова становится активным.
- Если экземпляр приложения простаивает в течение месяца (или вашего собственного периода неактивности), вам следует отменить его подписку на темы с помощью Firebase Admin SDK , удалив сопоставление токена с темой из бэкэнда FCM .
Преимущество этих двух шагов заключается в том, что рассылка токенов будет происходить быстрее, поскольку будет меньше устаревших токенов, а устаревшие экземпляры приложения автоматически подпишутся заново, как только снова станут активными.
Оцените успешность доставки.
Для получения наиболее точной картины доставки сообщений лучше всего отправлять сообщения только активно используемым экземплярам приложения. Это особенно важно, если вы регулярно отправляете сообщения в темы с большим количеством подписчиков; если часть этих подписчиков на самом деле неактивна, это может существенно повлиять на статистику доставки в долгосрочной перспективе.
Прежде чем направлять сообщения на определенный токен, следует учесть следующее:
- Указывают ли данные Google Analytics, данные, полученные в BigQuery, или другие сигналы отслеживания на то, что токен активен?
- Предыдущие попытки доставки неизменно терпели неудачу в течение определенного периода времени?
- Обновлялся ли регистрационный токен на ваших серверах за последний месяц?
- Сообщает ли API данных FCM о высоком проценте сбоев доставки сообщений на устройствах Android из-за
droppedDeviceInactive?
Для получения дополнительной информации о доставке см. раздел «Понимание доставки сообщений» .