Si vous utilisez les API FCM pour créer des requêtes d'envoi de manière programmatique, vous constaterez peut-être qu'au fil du temps, vous gaspillez des ressources en envoyant des messages à des appareils inactifs avec des jetons d'enregistrement obsolètes. Cette situation peut affecter les données de remise des messages indiquées dans la console Firebase ou les données exportées vers BigQuery, en affichant une baisse spectaculaire (mais en réalité non valide) des taux de remise. Ce guide présente certaines mesures que vous pouvez prendre pour vous assurer que le ciblage des messages est efficace et que les rapports sur la diffusion sont valides.
Jetons d'enregistrement obsolètes et expirés
Les jetons d'enregistrement obsolètes sont des jetons associés à des appareils inactifs qui ne se sont pas connectés à FCM depuis plus d'un mois. Au fil du temps, il devient de moins en moins probable que l'appareil se connecte à nouveau à FCM. Il est peu probable que les messages envoyés et les fan-outs de sujets pour ces jetons obsolètes soient un jour distribués.
Plusieurs raisons peuvent expliquer qu'un jeton devienne obsolète. Par exemple, l'appareil auquel le jeton est associé peut être perdu, détruit ou rangé et oublié.
Lorsque les jetons obsolètes atteignent 270 jours d'inactivité, FCM les considère comme des jetons expirés. Une fois qu'un jeton expire, FCM le marque comme non valide et refuse les envois vers celui-ci. Toutefois, FCM émet un nouveau jeton pour l'instance d'application dans le cas rare où l'appareil se reconnecte et où l'application est ouverte.
Bonnes pratiques de base
Il existe des pratiques fondamentales que vous devez suivre dans toute application qui utilise les API FCM pour créer des requêtes d'envoi de manière programmatique. Voici les principales bonnes pratiques :
- Récupérez les jetons d'enregistrement à partir de FCM et stockez-les sur votre serveur. Le serveur a pour rôle important de suivre le jeton de chaque client et de tenir à jour une liste des jetons actifs. Nous vous recommandons vivement d'implémenter un code temporel de jeton dans votre code et vos serveurs, et de mettre à jour ce code temporel à intervalles réguliers.
- Maintenez la fraîcheur des jetons et supprimez les jetons obsolètes. En plus de supprimer les jetons que FCM ne considère plus comme valides, vous pouvez surveiller d'autres signes indiquant que les jetons sont devenus obsolètes et les supprimer de manière proactive. Ce guide décrit certaines des options qui s'offrent à vous.
Récupérer et stocker des jetons d'enregistrement
Au démarrage initial de votre application, le SDK FCM génère un jeton d'enregistrement pour l'instance de l'application cliente. Il s'agit du jeton que vous devez inclure dans les requêtes d'envoi ciblé depuis l'API, ou ajouter aux abonnements aux thèmes pour cibler des thèmes.
Nous vous recommandons vivement de faire en sorte que votre application récupère ce jeton au démarrage initial et l'enregistre sur votre serveur d'application avec un code temporel. Ce code temporel doit être implémenté par votre code et vos serveurs, car il n'est pas fourni par les SDK FCM.
Il est également important d'enregistrer le jeton sur le serveur et de mettre à jour le code temporel chaque fois qu'il change, par exemple :
- L'application est restaurée sur un nouvel appareil
- L'utilisateur désinstalle ou réinstalle l'application
- L'utilisateur efface les données de l'application
- L'application redevient active une fois que FCM a expiré son jeton existant.
Exemple : stocker les jetons et les codes temporels dans Cloud Firestore
Par exemple, vous pouvez utiliser Cloud Firestore pour stocker des jetons dans une collection appelée fcmTokens
. Chaque ID de document de la collection correspond à un ID utilisateur. Le document stocke le jeton d'enregistrement actuel et son code temporel de dernière mise à jour. Utilisez la fonction set
comme indiqué dans cet exemple 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)
}
Chaque fois qu'un jeton est récupéré, il est stocké dans Cloud Firestore en appelant 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()
}
}
Maintenir la fraîcheur des jetons et supprimer les jetons obsolètes
Il n'est pas toujours simple de déterminer si un jeton est valide ou non. Pour couvrir tous les cas, vous devez adopter un seuil pour déterminer quand les jetons sont obsolètes. Par défaut, FCM considère qu'un jeton est obsolète si son instance d'application ne s'est pas connectée depuis un mois. Tout jeton datant de plus d'un mois est probablement un appareil inactif, car un appareil actif aurait actualisé son jeton.
Selon votre cas d'utilisation, un mois peut être trop court ou trop long. C'est donc à vous de déterminer les critères qui vous conviennent.
Détecter les réponses de jeton non valides du backend FCM
Assurez-vous de détecter les réponses de jetons non valides provenant de FCM et d'y répondre en supprimant de votre système tous les jetons d'enregistrement connus pour être non valides ou expirés. Avec l'API HTTP v1, ces messages d'erreur peuvent indiquer que votre requête d'envoi ciblait des jetons non valides ou expirés :
UNREGISTERED
(HTTP 404)INVALID_ARGUMENT
(HTTP 400)
Si vous êtes certain que la charge utile du message est valide et que vous recevez l'une de ces réponses pour un jeton cible, vous pouvez supprimer l'enregistrement de ce jeton, car il ne sera plus jamais valide. Par exemple, pour supprimer les jetons non valides de Cloud Firestore, vous pouvez déployer et exécuter une fonction comme celle-ci :
// 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 (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 ne renverra une réponse de jeton non valide que si un jeton a expiré après 270 jours ou si un client s'est explicitement désinscrit. Si vous avez besoin de suivre plus précisément la fraîcheur des données selon vos propres définitions, vous pouvez supprimer de manière proactive les jetons d'enregistrement obsolètes.
Mettre à jour les jetons régulièrement
Nous vous recommandons de récupérer et de mettre à jour régulièrement tous les jetons d'enregistrement sur votre serveur. Pour ce faire :
- Ajoutez la logique d'application dans votre application cliente pour récupérer le jeton actuel à l'aide de l'appel d'API approprié (par exemple,
token(completion):
pour les plates-formes Apple ougetToken()
pour Android), puis envoyez le jeton actuel au serveur de votre application pour qu'il soit stocké (avec un code temporel). Il peut s'agir d'un job mensuel configuré pour couvrir tous les clients ou jetons. - Ajoutez une logique de serveur pour mettre à jour le code temporel du jeton à intervalles réguliers, que le jeton ait changé ou non.
Pour obtenir un exemple de logique Android permettant de mettre à jour les jetons à l'aide de WorkManager, consultez Gestion des jetons Cloud Messaging sur le blog Firebase.
Quel que soit le calendrier que vous suivez, veillez à mettre à jour les jetons régulièrement. Une fréquence de mise à jour d'une fois par mois constitue un bon équilibre entre l'impact sur la batterie et la détection des jetons d'enregistrement inactifs. Cette actualisation permet également de s'assurer que tout appareil qui devient inactif actualise son enregistrement lorsqu'il redevient actif. Il n'est pas utile d'actualiser les données plus d'une fois par semaine.
Supprimer les jetons d'enregistrement obsolètes
Avant d'envoyer des messages à un appareil, assurez-vous que le code temporel du jeton d'enregistrement de l'appareil se trouve dans votre période de fraîcheur. Par exemple, vous pouvez implémenter Cloud Functions for Firebase pour exécuter une vérification quotidienne afin de vous assurer que le code temporel se trouve dans une période de fraîcheur définie, telle que const
EXPIRATION_TIME = 1000 * 60 * 60 * 24 * 30;
, puis supprimer les jetons obsolètes :
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(); });
});
Se désabonner des jetons obsolètes des thèmes
Si vous utilisez des thèmes, vous pouvez également annuler l'enregistrement des jetons obsolètes des thèmes auxquels ils sont abonnés. Pour ce faire, procédez comme suit :
- Votre application doit se réabonner aux thèmes une fois par mois et chaque fois que le jeton d'enregistrement change. Cela constitue une solution d'autoréparation, où les abonnements réapparaissent automatiquement lorsqu'une application redevient active.
- Si une instance d'application est inactive pendant un mois (ou votre propre période d'obsolescence), vous devez la désabonner des thèmes à l'aide du SDK Admin Firebase pour supprimer le mappage jeton/thème du backend FCM.
L'avantage de ces deux étapes est que vos fan-out se produiront plus rapidement, car il y aura moins de jetons obsolètes à distribuer. De plus, vos instances d'application obsolètes se réabonneront automatiquement une fois qu'elles seront à nouveau actives.
Mesurer la réussite des livraisons
Pour obtenir une vue plus précise de la distribution des messages, il est préférable de n'envoyer des messages qu'aux instances d'application activement utilisées. C'est particulièrement important si vous envoyez régulièrement des messages à des thèmes comptant un grand nombre d'abonnés. Si une partie de ces abonnés sont en fait inactifs, l'impact sur vos statistiques de distribution peut être important au fil du temps.
Avant de cibler des messages sur un jeton, tenez compte des points suivants :
- Les données Google Analytics, celles capturées dans BigQuery ou d'autres signaux de suivi indiquent-ils que le jeton est actif ?
- Les tentatives de livraison précédentes ont-elles échoué de manière répétée sur une période donnée ?
- Le jeton d'enregistrement a-t-il été mis à jour sur vos serveurs au cours du mois dernier ?
- Pour les appareils Android, l'API FCM Data signale-t-elle un pourcentage élevé d'échecs de distribution des messages en raison de
droppedDeviceInactive
?
Pour en savoir plus sur la distribution, consultez Comprendre la distribution des messages.