Migrar da API HTTP v1 do FCM para o AndroidConfigV2

Com o FCM AndroidConfigV2, é possível enviar mensagens orientadas por intent para dispositivos Android com entrega de notificações em tempo hábil e uma API simplificada.

Este guia aborda como migrar suas solicitações de envio de back-end do AndroidConfig para o AndroidConfigV2 e como atualizar os clientes do app Android do processamento legado do onMessageReceived para os novos callbacks de cliente v2 onNotificationReceived e onBackgroundSyncReceived.

Novidades no AndroidConfigV2

A API FCM AndroidConfigV2 simplifica a experiência do desenvolvedor ao introduzir dois tipos de mensagem:

  • RemoteNotification: recomendado para todas as mensagens do FCM que mostram uma notificação urgente e com prazo de validade visível para o usuário. Essas mensagens ignoram as medidas de economia de bateria e são entregues sem atraso. No dispositivo, o SDK do FCM e o Google Play Services assumem a responsabilidade de postar a notificação no NotificationManager do Android.
  • BackgroundSync: mensagens de dados não urgentes destinadas à sincronização em segundo plano e atualizações de estado do app. A entrega pode ser feita em lote ou atrasada para otimizar a duração da bateria.
Recurso Legado (AndroidConfig v1) Novo (AndroidConfigV2)
Campo de configuração message.android message.android_v2
Definição de prioridade priority: "high" ou priority: "normal" explícito Implícito pelo tipo de união (remote_notification para alertas urgentes versus background_sync para sincronização não urgente)
Postagem de notificações O app chama NotificationManager.notify() manualmente O SDK do FCM e o GMS Core postam diretamente ou usando o NotificationResponse.
Callbacks do cliente • onMessageReceived(RemoteMessage)
• onDeletedMessages()
• onNotificationReceived(RemoteMessage)
• onBackgroundSyncReceived(RemoteMessage)
• onDeletedMessages()
Recolhimento Variado; configuração explícita obrigatória Os dois tipos de mensagem podem ser recolhíveis ou não recolhíveis (não recolhíveis por padrão).

Autorizar solicitações de envio do AndroidConfigV2

Você pode continuar usando tokens de acesso do OAuth 2.0 derivados da sua conta de serviço do Firebase com o Application Default Credentials do Google ou o SDK Admin e enviar solicitações POST para o endpoint messages:send do HTTP v1.

Se você não estiver usando sua conta de serviço do Firebase, atribua sua conta dedicada ao papel de administrador da API Firebase Cloud Messaging (roles/firebase.messagingAdmin) e solicite o escopo do OAuth 2.0 https://www.googleapis.com/auth/firebase.messaging ao gerar tokens de acesso:

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

Atualizar solicitações de envio do servidor (REST)

Para enviar uma solicitação AndroidConfigV2 usando a API REST, substitua o objeto android no corpo da solicitação por android_v2. Em android_v2, especifique parâmetros comuns (como ttl, collapse_key, data e restricted_package_name) e forneça um dos seguintes campos de união: remote_notification ou background_sync.

Enviar uma mensagem RemoteNotification

Para enviar uma notificação urgente voltada ao usuário, inclua o objeto remote_notification e o payload notification recomendado. Por padrão, mutable_content é definido como falso. O GMS Core renderiza e publica imediatamente a notificação na bandeja do sistema sem executar o código do app em segundo plano.

Exemplo de JSON de solicitação HTTP completa (notificação renderizada automaticamente)

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

Enviar uma RemoteNotification com conteúdo mutável

Se o aplicativo precisar modificar a notificação no dispositivo antes de ser exibida, defina mutable_content: true dentro de remote_notification. Isso instrui o GMS Core a se vincular ao aplicativo cliente e invocar onNotificationReceived(). Isso é útil quando você quer descriptografar um payload criptografado de ponta a ponta, anexar avatares de usuário ou localizar strings.

Inclua um objeto notification dentro de remote_notification mesmo quando mutable_content for true. O GMS Core usa esse objeto notification como a notificação substituta se o callback do cliente expirar ou exceder os limites de supressão.

Se você estiver migrando de mensagens de dados legadas da v1 e quiser que o FCM entregue o payload para instalações de cliente mais antigas da v1 como uma mensagem de dados pura, defina use_as_v1_data_message: true dentro de remote_notification.

Exemplo de JSON de solicitação HTTP completa

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

Enviar uma mensagem de BackgroundSync

Para enviar uma mensagem não urgente para sincronização de dados em segundo plano, atualizações de estado silenciosas ou atualização de um selo ou widget de app, use o objeto background_sync. Não use essas mensagens para postar notificações visíveis ao usuário, porque elas podem ser agrupadas ou atrasadas para otimizar a duração da bateria do dispositivo.

Exemplo de JSON de solicitação HTTP completa

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

Atualizar callbacks do cliente Android

Para receber mensagens do AndroidConfigV2 no Android, atualize seu app para usar a versão mais recente do SDK do firebase-messaging. Atualize o serviço que estende FirebaseMessagingService implementando onNotificationReceived() e onBackgroundSyncReceived().

Instalar o SDK do EAP

Durante o EAP, o SDK do firebase-messaging v2 é distribuído como um arquivo de repositório Maven local, em vez de ser disponibilizado pelo repositório Maven público do Google.

  1. Adicione o diretório m2repository que contém firebase-messaging:26.1.0-eap01 ao diretório raiz do seu projeto do Android (<project-root>/m2repository) usando uma das seguintes opções:

    1. Opção A: fazer o download do arquivo pré-criado: faça o download de FirebaseMessaging_26.1.0-eap01.zip e extraia o diretório m2repository no diretório raiz do seu projeto do Android (<project-root>/m2repository).
    2. Opção B: criar no GitHub com o Gradle: clone a ramificação feature/fcm-android-config-v2-eap de firebase/firebase-android-sdk, crie o repositório Maven local e copie o diretório build/m2repository gerado para a raiz do projeto do 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. No settings.gradle.kts (ou settings.gradle) para envolvidos no projeto, adicione o diretório m2repository local à parte de cima do bloco repositories, acima 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. No app/build.gradle.kts (ou app/build.gradle) do módulo, atualize a dependência firebase-messaging para 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. Sincronize o projeto com arquivos do Gradle.

Processar RemoteNotifications com onNotificationReceived

Com AndroidConfigV2, as mensagens RemoteNotification são exibidas, a menos que mutable_content esteja definido como verdadeiro ou que sejam explicitamente silenciadas em onNotificationReceived().

Quando um RemoteNotification chega com mutable_content: true, o GMS Core se vincula ao aplicativo e chama onNotificationReceived(). O método precisa retornar um NotificationResponse em até 20 segundos.

Retornar uma notificação modificada

O objeto Notification transmitido para NotificationResponse.create substitui as propriedades de payload de substituição remote_notification.notification do servidor definidas na solicitação de envio. Para manter os valores da solicitação de envio, leia-os em message.notification (message.getNotification() em Java) ao aplicar modificações no 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());
    }
}

Suprimir uma notificação

Se o usuário estiver com a tela ou o chat relevante ativo no momento, você poderá suprimir a notificação retornando 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());
}

Notificações de transmissão ou cancelamento de alertas mais antigos (FcmNotificationManager)

Se você precisar postar várias notificações para um único push recebido ou cancelar uma notificação desatualizada antes de mostrar uma nova, use 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();
}

Processar mensagens de BackgroundSync usando onBackgroundSyncReceived

Para processar mensagens BackgroundSync no Android, use 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.
}

Processar exclusões de mensagens pendentes usando onDeletedMessages

Em algumas situações, como quando um dispositivo não se conecta ao FCM por um período prolongado ou quando a fila do servidor está cheia, o FCM pode excluir mensagens pendentes antes da entrega. Nesses casos, substitua 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();
}

Migrar apps específicos

Enviar notificações criptografadas de ponta a ponta (E2EE)

Ao enviar uma notificação criptografada de ponta a ponta usando um RemoteNotification, o conteúdo descriptografado permanece no app e na plataforma Android:

  1. Enviar notificação mutável: o servidor envia um RemoteNotification com mutable_content: true, o payload criptografado em data e um backup genérico não criptografado notification. O GMS Core envia a mensagem criptografada para o SDK de código aberto FCM no seu app.
  2. Descriptografar e retornar a notificação: o SDK do FCM invoca onNotificationReceived(). O app descriptografa o payload localmente, cria o Notification do Android descriptografado final e o transmite de volta ao SDK de código aberto FCM usando NotificationResponse ou FcmNotificationManager.
  3. Postar na plataforma e informar metadados: o SDK de código aberto do FCM posta a notificação descriptografada diretamente na API NotificationManager da plataforma Android e envia apenas metadados de notificação, como IDs de notificação e carimbos de data/hora, de volta ao GMS Core para observabilidade de entrega. O conteúdo descriptografado nunca é enviado ao GMS Core.
  4. Postar uma notificação de backup, se necessário: somente se o app não responder dentro do tempo limite de 20 segundos ou exceder os limites de supressão em segundo plano, o GMS Core vai postar o notification de backup genérico diretamente na plataforma Android.

Diagrama de arquitetura mostrando o fluxo de entrega de notificações criptografadas de ponta a ponta em quatro etapas entre o GMS Core, o SDK do FCM de código aberto, seu app e a API NotificationManager da plataforma Android.

Entender a supressão e os tempos limite de notificações

Para manter uma entrega confiável em todo o ecossistema Android, o GMS Core aplica tempos limite de processamento e limites de supressão de notificações ao processar mensagens RemoteNotification:

Tempo limite de processamento

Quando mutable_content é definido como true, o método onNotificationReceived() precisa retornar um NotificationResponse em até 20 segundos. Se a execução exceder esse limite, o callback será cancelado e o GMS Core vai postar automaticamente uma notificação de fallback renderizada do payload remote_notification.notification.

Taxa de supressão de notificações

Como as mensagens RemoteNotification ignoram as restrições do Doze para alertar os usuários imediatamente e são entregues sem atraso, os aplicativos precisam mostrar notificações para as mensagens RemoteNotification recebidas.

Cada aplicativo pode suprimir até 25% do tráfego de RemoteNotification enquanto é executado em segundo plano. Se a taxa de supressão de um aplicativo exceder 25%, o GMS Core vai renderizar e postar automaticamente uma notificação substituta renderizada do payload remote_notification.notification.

As supressões realizadas quando o aplicativo está ativo em primeiro plano não são contabilizadas nesse limite. Os aplicativos que recebem permissões de uso em segundo plano irrestrito também estão isentos dos limites de supressão em segundo plano.