Migra de la API de HTTP v1 de FCM a AndroidConfigV2

Con la FCM AndroidConfigV2, puedes enviar mensajes basados en intents a dispositivos Android con una entrega de notificaciones oportuna y una API simplificada.

En esta guía, se explica cómo migrar las solicitudes de envío de tu backend de AndroidConfig a AndroidConfigV2 y cómo actualizar los clientes de tu aplicación para Android desde el manejo heredado de onMessageReceived a las nuevas devoluciones de llamada del cliente de la versión 2 onNotificationReceived y onBackgroundSyncReceived.

Novedades de AndroidConfigV2

La API de AndroidConfigV2 de FCM simplifica la experiencia de los desarrolladores, ya que presenta dos tipos de mensajes:

  • RemoteNotification: Se recomienda para todos los mensajes de FCM que muestran una notificación urgente y urgente visible para el usuario. Estos mensajes omiten las medidas de ahorro de batería y se entregan sin demora. En el dispositivo, el SDK de FCM y los Servicios de Google Play se encargan de publicar la notificación en el NotificationManager de Android.
  • BackgroundSync: Son mensajes de datos no urgentes destinados a la sincronización en segundo plano y las actualizaciones del estado de la app. La entrega se puede realizar por lotes o retrasarse para optimizar la duración de la batería.
Función Heredada (AndroidConfig v1) Nuevo (AndroidConfigV2)
Campo de configuración message.android message.android_v2
Definición de prioridad priority: "high" o priority: "normal" explícito Implícito por el tipo de unión (remote_notification para alertas urgentes en comparación con background_sync para la sincronización no urgente)
Publicación de notificaciones La app llama a NotificationManager.notify() de forma manual El SDK de FCM y los Servicios de Google para dispositivos móviles publican directamente o con NotificationResponse
Devoluciones de llamada del cliente • onMessageReceived(RemoteMessage)
• onDeletedMessages()
• onNotificationReceived(RemoteMessage)
• onBackgroundSyncReceived(RemoteMessage)
• onDeletedMessages()
Contracción Varía; se requiere un parámetro de configuración explícito Ambos tipos de mensajes pueden ser contraíbles o no contraíbles (no contraíbles de forma predeterminada).

Autoriza solicitudes de envío de AndroidConfigV2

Puedes seguir usando los tokens de acceso de OAuth 2.0 derivados de tu cuenta de servicio de Firebase con las credenciales predeterminadas de la aplicación de Google o el SDK de Admin, y enviar solicitudes POST al extremo messages:send de HTTP v1.

Si no usas tu cuenta de servicio de Firebase, asegúrate de asignar tu cuenta dedicada al rol de administrador de la API de Firebase Cloud Messaging (roles/firebase.messagingAdmin) y solicita el permiso de OAuth 2.0 https://www.googleapis.com/auth/firebase.messaging cuando crees tokens de acceso:

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

Actualiza las solicitudes de envío del servidor (REST)

Para enviar una solicitud AndroidConfigV2 con la API de REST, reemplaza el objeto android en el cuerpo de la solicitud por android_v2. En android_v2, especifica los parámetros comunes (como ttl, collapse_key, data y restricted_package_name) y proporciona uno de los siguientes campos de unión: remote_notification o background_sync.

Envía un mensaje RemoteNotification

Para enviar una notificación urgente para el usuario, incluye el objeto remote_notification y su carga útil notification recomendada. De forma predeterminada, mutable_content se establece en falso. Los Servicios de Google para dispositivos móviles renderizan y publican de inmediato la notificación en la bandeja del sistema sin ejecutar el código de la app en segundo plano.

Ejemplo completo de JSON de solicitud HTTP (notificación renderizada automáticamente)

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
        }
      }
    }
  }
}

Envía una RemoteNotification con contenido mutable

Si tu aplicación necesita modificar la notificación en el dispositivo antes de que se muestre, establece mutable_content: true dentro de remote_notification. Esto le indica a GMS Core que se vincule a tu aplicación cliente y que invoque onNotificationReceived(). Esto es útil cuando quieres desencriptar una carga útil encriptada de extremo a extremo, adjuntar avatares de usuario o localizar cadenas.

Asegúrate de incluir un objeto notification dentro de remote_notification, incluso cuando mutable_content sea true. Los Servicios de GMS usan este objeto notification como la notificación de resguardo si se agota el tiempo de espera de la devolución de llamada del cliente o se superan los límites de supresión.

Si migras desde mensajes de datos heredados de la versión 1 y quieres que FCM entregue la carga útil a instalaciones de clientes de la versión 1 más antiguas como un mensaje de datos puro, establece use_as_v1_data_message: true dentro de remote_notification.

Ejemplo completo de JSON de solicitud HTTP

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
        }
      }
    }
  }
}

Envía un mensaje de BackgroundSync

Para enviar un mensaje no urgente para la sincronización de datos en segundo plano, las actualizaciones de estado silenciosas o la actualización de un widget o una insignia de la app, usa el objeto background_sync. No uses estos mensajes para publicar notificaciones visibles para el usuario, ya que se pueden agrupar por lotes o retrasar para optimizar la duración de batería del dispositivo.

Ejemplo completo de JSON de solicitud HTTP

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": {}
    }
  }
}

Actualiza las devoluciones de llamada del cliente de Android

Para recibir mensajes de AndroidConfigV2 en Android, actualiza tu app para que use el SDK de firebase-messaging más reciente. Actualiza tu servicio extendiendo FirebaseMessagingService con la implementación de onNotificationReceived() y onBackgroundSyncReceived().

Instala el SDK del EAP

Durante el EAP, el SDK de firebase-messaging v2 se distribuye como un archivo de repositorio de Maven local, en lugar de hacerlo a través del repositorio público de Maven de Google.

  1. Agrega el directorio m2repository que contiene firebase-messaging:26.1.0-eap01 al directorio raíz de tu proyecto de Android (<project-root>/m2repository) con una de las siguientes opciones:

    1. Opción A: Descarga el archivo compilado previamente: Descarga FirebaseMessaging_26.1.0-eap01.zip y extrae el directorio m2repository en el directorio raíz de tu proyecto de Android (<project-root>/m2repository).
    2. Opción B: Compila desde GitHub con Gradle: Clona la rama feature/fcm-android-config-v2-eap de firebase/firebase-android-sdk, compila el repositorio de Maven local y copia el directorio build/m2repository generado en la raíz de tu proyecto de 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. En el archivo settings.gradle.kts (o settings.gradle) de nivel de proyecto, agrega el directorio local m2repository a la parte superior del bloque repositories, por encima 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. En el archivo app/build.gradle.kts (o app/build.gradle) a nivel del módulo, actualiza la dependencia de firebase-messaging a 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. Sincroniza tu proyecto con archivos de Gradle.

Controla RemoteNotifications con onNotificationReceived

Con AndroidConfigV2, se muestran los mensajes de RemoteNotification, a menos que mutable_content se establezca como verdadero o que se silencien de forma explícita en onNotificationReceived().

Cuando llega un RemoteNotification con mutable_content: true, GMS Core se vincula a tu aplicación y llama a onNotificationReceived(). Tu método debe devolver un NotificationResponse en un plazo de 20 segundos.

Cómo devolver una notificación modificada

El objeto Notification que pasas a NotificationResponse.create reemplaza las propiedades de carga útil de resguardo remote_notification.notification del servidor definidas en la solicitud de envío. Para conservar los valores de la solicitud de envío, léelos de message.notification (message.getNotification() en Java) mientras aplicas las modificaciones integrado en el dispositivo:

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());
    }
}

Cómo suprimir una notificación

Si el usuario tiene la pantalla o el chat pertinentes activos en este momento, puedes suprimir la notificación devolviendo 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());
}

Transmite notificaciones o cancela alertas más antiguas (FcmNotificationManager)

Si necesitas publicar varias notificaciones para un solo mensaje push entrante o cancelar una notificación inactiva antes de mostrar una nueva, usa 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();
}

Controla los mensajes de BackgroundSync con onBackgroundSyncReceived

Para procesar mensajes de BackgroundSync en Android, usa 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.
}

Controla las eliminaciones de mensajes pendientes con onDeletedMessages

En algunas situaciones, como cuando un dispositivo no se conectó a FCM durante un período prolongado o cuando la cola del servidor está llena, FCM puede borrar los mensajes pendientes antes de la entrega. En estos casos, anula 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();
}

Cómo migrar apps específicas

Envía notificaciones con encriptación de extremo a extremo (E2EE)

Cuando se entrega una notificación encriptada de extremo a extremo con un RemoteNotification, el contenido de la notificación desencriptada permanece dentro de tu app y de la plataforma de Android:

  1. Envía una notificación mutable: Tu servidor envía un RemoteNotification con mutable_content: true, la carga útil encriptada en data y una copia de seguridad genérica sin encriptar notification. Los Servicios de Google Play entregan el mensaje encriptado al SDK de FCM de código abierto dentro de tu app.
  2. Desencripta y devuelve la notificación: El SDK de FCM invoca a onNotificationReceived(). Tu app desencripta la carga útil de forma local, compila el Notification de Android desencriptado final y lo pasa de vuelta al SDK de código abierto de FCM con NotificationResponse o FcmNotificationManager.
  3. Publica en la plataforma y genera informes de metadatos: El SDK de código abierto de FCM publica la notificación desencriptada directamente en la API de NotificationManager de la plataforma de Android y envía solo los metadatos de la notificación, como los IDs de notificación y las marcas de tiempo, de vuelta a los Servicios de Google Play para la observabilidad de la entrega. El contenido desencriptado nunca se envía a GMS Core.
  4. Publica una notificación de copia de seguridad si es necesario: Solo si tu app no responde en el tiempo de espera de 20 segundos o supera los límites de supresión en segundo plano, los Servicios de GMS publican la notification de copia de seguridad genérica directamente en la plataforma de Android.

Diagrama de arquitectura que muestra el flujo de entrega de notificaciones encriptadas de extremo a extremo de 4 pasos entre los Servicios de GMS, el SDK de FCM de código abierto, tu app y la API de NotificationManager de la plataforma de Android.

Información sobre la supresión y los tiempos de espera de las notificaciones

Para mantener una entrega confiable en todo el ecosistema Android, GMS Core aplica tiempos de espera de procesamiento y límites de supresión de notificaciones cuando se manejan mensajes de RemoteNotification:

Tiempo de espera de procesamiento

Cuando mutable_content se establece en true, tu método onNotificationReceived() debe devolver un NotificationResponse en un plazo de 20 segundos. Si la ejecución supera este umbral, se cancela la devolución de llamada y los Servicios de Google Play publican automáticamente una notificación de resguardo renderizada a partir de la carga útil de remote_notification.notification.

Tasa de supresión de notificaciones

Dado que los mensajes de RemoteNotification omiten las restricciones de Doze para alertar a los usuarios de inmediato y se entregan sin demora, se espera que las aplicaciones muestren notificaciones de los mensajes de RemoteNotification recibidos.

Cada aplicación puede suprimir hasta un 25% de su tráfico de RemoteNotification mientras se ejecuta en segundo plano. Si la tasa de supresión de una aplicación supera el 25%, los Servicios de Google para dispositivos móviles renderizan y publican automáticamente una notificación de resguardo renderizada a partir de la carga útil de remote_notification.notification.

Las eliminaciones que se realizan cuando la aplicación está activa en primer plano no se tienen en cuenta para este umbral. Las aplicaciones a las que se les otorgan permisos de uso en segundo plano sin restricciones también están exentas de los límites de supresión en segundo plano.