Migrer de l'API FCM HTTP v1 vers AndroidConfigV2

FCM AndroidConfigV2 vous permet d'envoyer des messages basés sur l'intent aux appareils Android, avec une distribution des notifications en temps opportun et une API simplifiée.

Ce guide explique comment migrer vos requêtes d'envoi de backend de AndroidConfig vers AndroidConfigV2, et comment mettre à jour vos clients d'application Android pour passer de l'ancienne gestion onMessageReceived aux nouveaux rappels client v2 onNotificationReceived et onBackgroundSyncReceived.

Nouveautés d'AndroidConfigV2

L'API FCM AndroidConfigV2 simplifie l'expérience des développeurs en introduisant deux types de messages :

  • RemoteNotification : recommandé pour tous les messages FCM qui affichent une notification urgente, sensible au facteur temps et visible par l'utilisateur. Ces messages contournent toutes les mesures d'économie de batterie et sont distribués sans délai. Sur l'appareil, le SDK FCM et les services Google Play se chargent de publier la notification dans le NotificationManager d'Android.
  • BackgroundSync : messages de données non urgents destinés à la synchronisation en arrière-plan et aux mises à jour de l'état de l'application. La distribution peut être groupée ou retardée pour optimiser l'autonomie de la batterie.
Fonctionnalité Ancienne (AndroidConfig v1) Nouveau (AndroidConfigV2)
Champ de configuration message.android message.android_v2
Définition de la priorité priority: "high" ou priority: "normal" explicite Implicite par type d'union (remote_notification pour les alertes urgentes et background_sync pour la synchronisation non urgente)
Publication de notifications L'application appelle manuellement NotificationManager.notify() Publiez directement le SDK FCM et GMS Core, ou utilisez NotificationResponse.
Rappels client • onMessageReceived(RemoteMessage)
• onDeletedMessages()
• onNotificationReceived(RemoteMessage)
• onBackgroundSyncReceived(RemoteMessage)
• onDeletedMessages()
Réductibilité Variable ; paramètre explicite requis Les deux types de messages peuvent être réductibles ou non (non réductibles par défaut).

Autoriser les requêtes d'envoi AndroidConfigV2

Vous pouvez continuer à utiliser les jetons d'accès OAuth 2.0 dérivés de votre compte de service Firebase à l'aide des identifiants par défaut de l'application Google ou du SDK Admin, et envoyer des requêtes POST au point de terminaison HTTP v1 messages:send.

Si vous n'utilisez pas votre compte de service Firebase, veillez à attribuer à votre compte dédié le rôle Administrateur de l'API Firebase Cloud Messaging (roles/firebase.messagingAdmin) et à demander le champ d'application OAuth 2.0 https://www.googleapis.com/auth/firebase.messaging lors de la création des jetons d'accès :

POST https://fcm.googleapis.com/v1/projects/YOUR_PROJECT_ID/messages:send HTTP/1.1
Authorization: Bearer YOUR_OAUTH_ACCESS_TOKEN
Content-Type: application/json

Mettre à jour les requêtes d'envoi du serveur (REST)

Pour envoyer une requête AndroidConfigV2 à l'aide de l'API REST, remplacez l'objet android dans le corps de votre requête par android_v2. Dans android_v2, spécifiez les paramètres courants (tels que ttl, collapse_key, data et restricted_package_name), puis indiquez l'un des champs d'union suivants : remote_notification ou background_sync.

Envoyer un message RemoteNotification

Pour envoyer une notification urgente visible par l'utilisateur, incluez l'objet remote_notification et sa charge utile notification recommandée. Par défaut, mutable_content est défini sur "false". GMS Core affiche et publie immédiatement la notification dans la barre d'état système sans exécuter le code de l'application en arrière-plan.

Exemple de requête HTTP JSON complète (notification affichée automatiquement)

POST https://fcm.googleapis.com/v1/projects/myproject-b5ae1/messages:send HTTP/1.1
Content-Type: application/json
Authorization: Bearer ya29.ElqKBGN2Ri_Uz...HnS_uNreA

{
  "message": {
    "token": "bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
    "notification": {
      "title": "FCM Message",
      "body": "This is an FCM notification message!"
    },
    "android_v2": {
      "ttl": "3600s",
      "remote_notification": {
        "mutable_content": false,
        "notification": {
          "title": "Order update",
          "body": "Your order has been shipped!",
          "icon": "ic_notification_custom",
          "sound": "default",
          "tag": "alert_tag_1",
          "id": 100
        }
      }
    }
  }
}

Envoyer une RemoteNotification avec du contenu modifiable

Si votre application doit modifier la notification sur l'appareil avant qu'elle ne s'affiche, définissez mutable_content: true dans remote_notification. Cela indique à GMS Core de se lier à votre application cliente et d'appeler onNotificationReceived(). Cela est utile lorsque vous souhaitez déchiffrer une charge utile chiffrée de bout en bout, joindre des avatars d'utilisateurs ou localiser des chaînes.

Veillez à inclure un objet notification dans remote_notification, même lorsque mutable_content est défini sur true. GMS Core utilise cet objet notification comme notification de remplacement si le rappel du client expire ou dépasse les limites de suppression.

Si vous migrez depuis d'anciens messages de données v1 et que vous souhaitez que FCM transmette la charge utile aux anciennes installations clientes v1 en tant que message de données pur, définissez use_as_v1_data_message: true dans remote_notification.

Exemple de requête HTTP JSON complète

POST https://fcm.googleapis.com/v1/projects/myproject-b5ae1/messages:send HTTP/1.1
Content-Type: application/json
Authorization: Bearer ya29.ElqKBGN2Ri_Uz...HnS_uNreA

{
  "message": {
    "token": "bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
    "data": {
      "encrypted_content": "U2FsdGVkX19vN2m+",
      "sender_id": "user_98124"
    },
    "android_v2": {
      "ttl": "3600s",
      "remote_notification": {
        "mutable_content": true,
        "use_as_v1_data_message": true,
        "notification": {
          "title": "New message",
          "body": "You have a new message",
          "tag": "chat_alert",
          "id": 1001
        }
      }
    }
  }
}

Envoyer un message BackgroundSync

Pour envoyer un message non urgent pour la synchronisation des données en arrière-plan, les mises à jour d'état silencieuses ou la mise à jour d'un badge ou d'un widget d'application, utilisez l'objet background_sync. N'utilisez pas ces messages pour publier des notifications visibles par l'utilisateur, car elles peuvent être regroupées ou retardées pour optimiser l'autonomie de la batterie de l'appareil.

Exemple de requête HTTP JSON complète

POST https://fcm.googleapis.com/v1/projects/myproject-b5ae1/messages:send HTTP/1.1
Content-Type: application/json
Authorization: Bearer ya29.ElqKBGN2Ri_Uz...HnS_uNreA

{
  "message": {
    "token": "bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
    "data": {
      "score": "FETCH_UPDATES",
      "sync_action": "FETCH_UPDATES"
    },
    "android_v2": {
      "ttl": "86400s",
      "collapse_key": "app_state_sync",
      "background_sync": {}
    }
  }
}

Mettre à jour les rappels du client Android

Pour recevoir des messages AndroidConfigV2 sur Android, mettez à niveau votre application afin d'utiliser le dernier SDK firebase-messaging. Mettez à jour votre service en étendant FirebaseMessagingService en implémentant onNotificationReceived() et onBackgroundSyncReceived().

Installer le SDK EAP

Pendant le programme d'accès anticipé, le SDK firebase-messaging v2 est distribué sous forme d'archive de dépôt Maven local plutôt que via le dépôt Maven public de Google.

  1. Ajoutez le répertoire m2repository contenant firebase-messaging:26.1.0-eap01 au répertoire racine de votre projet Android (<project-root>/m2repository) à l'aide de l'une des options suivantes :

    1. Option A : Télécharger l'archive prédéfinie : téléchargez FirebaseMessaging_26.1.0-eap01.zip et extrayez le répertoire m2repository dans le répertoire racine de votre projet Android (<project-root>/m2repository).
    2. Option B : compiler à partir de GitHub avec Gradle : clonez la branche feature/fcm-android-config-v2-eap de firebase/firebase-android-sdk, compilez le dépôt Maven local et copiez le répertoire build/m2repository généré à la racine de votre projet Android :

      git clone --branch feature/fcm-android-config-v2-eap --single-branch \
        https://github.com/firebase/firebase-android-sdk.git
      cd firebase-android-sdk
      ./gradlew -PprojectsToPublish="firebase-messaging" firebasePublish
      cp -R build/m2repository /path/to/your/android-project/m2repository
      
  2. Dans le fichier settings.gradle.kts (ou settings.gradle) au niveau du projet, ajoutez le répertoire m2repository local en haut du bloc repositories, au-dessus de google() :

    Kotlin

    dependencyResolutionManagement {
        repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
        repositories {
            maven { url = uri("${rootDir}/m2repository") }
            google()
            mavenCentral()
        }
    }
    

    Groovy

    dependencyResolutionManagement {
        repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
        repositories {
            maven { url "${rootDir}/m2repository" }
            google()
            mavenCentral()
        }
    }
    
  3. Dans le fichier app/build.gradle.kts (ou app/build.gradle) au niveau du module, remplacez votre dépendance firebase-messaging par 26.1.0-eap01 :

    Kotlin

    dependencies {
        implementation("com.google.firebase:firebase-messaging:26.1.0-eap01")
    }
    

    Groovy

    dependencies {
        implementation 'com.google.firebase:firebase-messaging:26.1.0-eap01'
    }
    
  4. Synchronisez votre projet avec les fichiers Gradle.

Gérer les RemoteNotifications avec onNotificationReceived

Avec AndroidConfigV2, les messages RemoteNotification sont affichés, sauf si mutable_content est défini sur "true" ou s'ils sont explicitement désactivés dans onNotificationReceived().

Lorsqu'un RemoteNotification arrive avec mutable_content: true, GMS Core se lie à votre application et appelle onNotificationReceived(). Votre méthode doit renvoyer un NotificationResponse dans un délai de 20 secondes.

Renvoyer une notification modifiée

L'objet Notification que vous transmettez à NotificationResponse.create remplace les propriétés de charge utile de remplacement remote_notification.notification du serveur définies dans la requête d'envoi. Pour conserver les valeurs de la requête d'envoi, lisez-les à partir de message.notification (message.getNotification() en Java) tout en appliquant les modifications sur l'appareil :

Kotlin

import androidx.core.app.NotificationCompat
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.NotificationResponse
import com.google.firebase.messaging.RemoteMessage

class MyFirebaseMessagingService : FirebaseMessagingService() {

    override fun onNotificationReceived(message: RemoteMessage): NotificationResponse {
        val serverNotif = message.notification
        val channelId = serverNotif?.channelId ?: "default_channel"
        // Keep the server-defined title unchanged, or fall back to default
        val title = serverNotif?.title ?: "New message"
        // Modify the notification body on-device (for example, decrypting data)
        val decryptedBody = decryptPayload(message.data["encrypted_content"])

        val builder = NotificationCompat.Builder(this, channelId)
            .setSmallIcon(R.drawable.ic_stat_notification)
            .setContentTitle(title)
            .setContentText(decryptedBody)
            .setPriority(NotificationCompat.PRIORITY_HIGH)
            .setAutoCancel(true)

        // Return the response for the open-source FCM SDK to post to the Android platform
        return NotificationResponse.create("optional_tag", 100, builder.build())
    }
}

Java

import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;
import com.google.firebase.messaging.NotificationResponse;
import androidx.core.app.NotificationCompat;

public class MyFirebaseMessagingService extends FirebaseMessagingService {

    @Override
    public NotificationResponse onNotificationReceived(RemoteMessage message) {
        RemoteMessage.Notification serverNotif = message.getNotification();
        String channelId = (serverNotif != null && serverNotif.getChannelId() != null)
                ? serverNotif.getChannelId()
                : "default_channel";
        // Keep the server-defined title unchanged, or fall back to default
        String title = (serverNotif != null && serverNotif.getTitle() != null)
                ? serverNotif.getTitle()
                : "New message";
        // Modify the notification body on-device (for example, decrypting data)
        String decryptedBody = decryptPayload(message.getData().get("encrypted_content"));

        NotificationCompat.Builder builder = new NotificationCompat.Builder(this, channelId)
                .setSmallIcon(R.drawable.ic_stat_notification)
                .setContentTitle(title)
                .setContentText(decryptedBody)
                .setPriority(NotificationCompat.PRIORITY_HIGH)
                .setAutoCancel(true);

        // Return the response for the open-source FCM SDK to post to the Android platform
        return NotificationResponse.create("optional_tag", 100, builder.build());
    }
}

Supprimer une notification

Si l'utilisateur a actuellement l'écran ou le chat approprié actif, vous pouvez supprimer la notification en renvoyant NotificationResponse.suppress() :

Kotlin

import androidx.lifecycle.Lifecycle
import androidx.lifecycle.ProcessLifecycleOwner

override fun onNotificationReceived(message: RemoteMessage): NotificationResponse {
    val chatId = message.data["chat_id"]
    val isAppForeground = ProcessLifecycleOwner.get()
        .lifecycle
        .currentState
        .isAtLeast(Lifecycle.State.STARTED)

    if (isAppForeground && chatRepository.isConversationActive(chatId)) {
        // Suppress notification display because the user is actively viewing this chat
        return NotificationResponse.suppress()
    }

    val builder = buildNotificationForChat(message)
    return NotificationResponse.create("chat_tag", 101, builder.build())
}

Java

import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.ProcessLifecycleOwner;

@Override
public NotificationResponse onNotificationReceived(RemoteMessage message) {
    String chatId = message.getData().get("chat_id");
    boolean isAppForeground = ProcessLifecycleOwner.get()
            .getLifecycle()
            .getCurrentState()
            .isAtLeast(Lifecycle.State.STARTED);

    if (isAppForeground && chatRepository.isConversationActive(chatId)) {
        // Suppress notification display because the user is actively viewing this chat
        return NotificationResponse.suppress();
    }

    NotificationCompat.Builder builder = buildNotificationForChat(message);
    return NotificationResponse.create("chat_tag", 101, builder.build());
}

Diffuser des notifications ou annuler des alertes plus anciennes (FcmNotificationManager)

Si vous devez publier plusieurs notifications pour un seul push entrant ou annuler une notification obsolète avant d'en afficher une nouvelle, utilisez FcmNotificationManager :

Kotlin

override fun onNotificationReceived(message: RemoteMessage): NotificationResponse {
    // Retrieve the scoped manager for this message execution
    val manager = getFcmNotificationManager(message)

    // 1. Cancel an outdated notification right away
    manager.cancelNow("message_tag", 50)

    // 2. Post the item notification immediately
    val itemNotif = NotificationCompat.Builder(this, "chat_channel")
        .setSmallIcon(R.drawable.ic_message)
        .setContentTitle("Alex")
        .setContentText(message.data["text"])
    manager.postNow("item_tag", 51, itemNotif.build())

    // 3. Post a group summary notification
    val summaryNotif = NotificationCompat.Builder(this, "chat_channel")
        .setSmallIcon(R.drawable.ic_message)
        .setContentTitle("New Conversations")
        .setGroupSummary(true)
    manager.postNow("summary_tag", 52, summaryNotif.build())

    // 4. Return finished() to complete the streaming workflow
    return manager.finished()
}

Java

@Override
public NotificationResponse onNotificationReceived(RemoteMessage message) {
    // Retrieve the scoped manager for this message execution
    FcmNotificationManager manager = getFcmNotificationManager(message);

    // 1. Cancel an outdated notification right away
    manager.cancelNow("message_tag", 50);

    // 2. Post the item notification immediately
    NotificationCompat.Builder itemNotif = new NotificationCompat.Builder(this, "chat_channel")
            .setSmallIcon(R.drawable.ic_message)
            .setContentTitle("Alex")
            .setContentText(message.getData().get("text"));
    manager.postNow("item_tag", 51, itemNotif.build());

    // 3. Post a group summary notification
    NotificationCompat.Builder summaryNotif = new NotificationCompat.Builder(this, "chat_channel")
            .setSmallIcon(R.drawable.ic_message)
            .setContentTitle("New Conversations")
            .setGroupSummary(true);
    manager.postNow("summary_tag", 52, summaryNotif.build());

    // 4. Return finished() to complete the streaming workflow
    return manager.finished();
}

Gérer les messages BackgroundSync à l'aide de onBackgroundSyncReceived

Pour traiter les messages BackgroundSync sur Android, utilisez onBackgroundSyncReceived() :

Kotlin

override fun onBackgroundSyncReceived(message: RemoteMessage) {
    val syncCommand = message.data["sync_action"]

    try {
        // Perform local database synchronization or cache refresh
        AppDataRepository.getInstance(applicationContext).syncWithServer(syncCommand)
    } catch (e: Exception) {
        Log.e("FCM", "Background sync operation failed", e)
    }
    // Execution completes -> GMS Core marks the message as handled and unbinds.
}

Java

@Override
public void onBackgroundSyncReceived(RemoteMessage message) {
    String syncCommand = message.getData().get("sync_action");

    try {
        // Perform local database synchronization or cache refresh
        AppDataRepository.getInstance(getApplicationContext()).syncWithServer(syncCommand);
    } catch (Exception e) {
        Log.e("FCM", "Background sync operation failed", e);
    }
    // Execution completes -> GMS Core marks the message as handled and unbinds.
}

Gérer les suppressions de messages en attente à l'aide de onDeletedMessages

Dans certains cas, par exemple lorsqu'un appareil ne s'est pas connecté à FCM pendant une période prolongée ou lorsque la file d'attente du serveur est pleine, FCM peut supprimer les messages en attente avant leur distribution. Dans ce cas, remplacez onDeletedMessages() :

Kotlin

override fun onDeletedMessages() {
    // Perform a full state synchronization with your app server
    AppDataRepository.getInstance(applicationContext).scheduleFullSync()
}

Java

@Override
public void onDeletedMessages() {
    // Perform a full state synchronization with your app server
    AppDataRepository.getInstance(getApplicationContext()).scheduleFullSync();
}

Migrer des applications spécifiques

Envoyer des notifications chiffrées de bout en bout (E2EE)

Lors de l'envoi d'une notification chiffrée de bout en bout à l'aide d'un RemoteNotification, le contenu de la notification déchiffrée reste dans votre application et sur la plate-forme Android :

  1. Envoyer une notification modifiable : votre serveur envoie un RemoteNotification avec mutable_content: true, la charge utile chiffrée dans data et une sauvegarde générique non chiffrée notification. GMS Core transmet le message chiffré au SDK FCM Open Source dans votre application.
  2. Déchiffrer et renvoyer la notification : le SDK FCM appelle onNotificationReceived(). Votre application déchiffre la charge utile localement, crée le Notification Android déchiffré final et le transmet au SDK FCM Open Source à l'aide de NotificationResponse ou FcmNotificationManager.
  3. Publier sur la plate-forme et signaler les métadonnées : le SDK FCM Open Source publie la notification déchiffrée directement dans l'API NotificationManager de la plate-forme Android et n'envoie que les métadonnées de notification, telles que les ID de notification et les codes temporels, à GMS Core pour l'observabilité de la diffusion. Le contenu déchiffré n'est jamais envoyé à GMS Core.
  4. Publier une notification de sauvegarde si nécessaire : GMS Core ne publie la notification de sauvegarde générique notification directement sur la plate-forme Android que si votre application ne répond pas dans le délai de 20 secondes ou dépasse les limites de suppression en arrière-plan.

Schéma d'architecture illustrant le flux de distribution des notifications chiffrées de bout en bout en quatre étapes entre GMS Core, le SDK FCM Open Source, votre application et l'API NotificationManager de la plate-forme Android.

Comprendre la suppression et les délais avant expiration des notifications

Pour assurer une distribution fiable dans l'écosystème Android, GMS Core applique des délais avant expiration du traitement et des limites de suppression des notifications lors du traitement des messages RemoteNotification :

Délai avant expiration du traitement

Lorsque mutable_content est défini sur true, votre méthode onNotificationReceived() doit renvoyer un NotificationResponse dans un délai de 20 secondes. Si l'exécution dépasse ce seuil, le rappel est annulé et GMS Core publie automatiquement une notification de remplacement affichée à partir de la charge utile remote_notification.notification.

Taux de suppression des notifications

Étant donné que les messages RemoteNotification contournent les restrictions du mode Sommeil pour alerter immédiatement les utilisateurs et sont distribués sans délai, les applications doivent afficher des notifications pour les messages RemoteNotification reçus.

Chaque application est autorisée à supprimer jusqu'à 25% de son trafic RemoteNotification lorsqu'elle s'exécute en arrière-plan. Si le taux de suppression d'une application dépasse 25%, GMS Core affiche et publie automatiquement une notification de remplacement à partir de la charge utile remote_notification.notification.

Les suppressions effectuées lorsque l'application est active au premier plan ne sont pas comptabilisées dans ce seuil. Les applications disposant d'autorisations d'utilisation en arrière-plan sans restriction sont également exemptées des limites de suppression en arrière-plan.