Receber mensagens usando o Firebase Cloud Messaging

Este guia descreve como configurar o Firebase Cloud Messaging nos seus apps cliente para dispositivos móveis e Web para que você possa receber mensagens de forma confiável.

Para receber mensagens, use um serviço que amplie o FirebaseMessagingService. Seu serviço precisa substituir as callbacks onMessageReceived e onDeletedMessages.

O onMessageReceived está disponível para a maioria dos tipos de mensagens, com as seguintes exceções:

  • Mensagens de notificação entregues quando seu app estiver em segundo plano. Nesse caso, a notificação é entregue à bandeja do sistema do dispositivo. Quando um usuário toca na notificação, a tela de início do app é aberta por padrão.

  • Mensagens com payload de notificação e dados, quando recebidas em segundo plano. Nesse caso, a notificação é entregue à bandeja do sistema do dispositivo, e o payload de dados é entregue nos extras da intent da atividade da sua tela de início.

Em resumo:

Estado do app Notificação Dados Ambos
Primeiro plano onMessageReceived onMessageReceived onMessageReceived
Segundo plano Bandeja do sistema onMessageReceived Notificação: bandeja do sistema Dados: nos extras do intent.

Para mais informações sobre os tipos de mensagens, consulte Notificações e mensagens de dados.

O callback onMessageReceived recebe tempos limite que permitem postar uma notificação, mas os timers não foram projetados para permitir que o app acesse a rede ou faça trabalho adicional. Assim, se o app fizer algo mais complicado, você precisará fazer um trabalho adicional para garantir que ele possa concluir a tarefa.

Se você acha que o app pode levar quase 10 segundos para processar uma mensagem, programe um job do WorkManager ou siga as orientações do WakeLock abaixo. Em alguns casos, a janela de tempo em que a mensagem pode ser processada pode ser menor do que 10 segundos dependendo dos atrasos incorridos antes de chamar onMessageReceived, incluindo atrasos do SO, o tempo de inicialização do aplicativo, a linha de execução principal bloqueada por outras operações ou onMessageReceived chamadas estão demorando muito. Depois que o timer expirar, o app poderá estar sujeito à eliminação de processos ou a limites de execução em segundo plano. As latências de transações de rede e inicialização de apps podem ser significativas. Portanto, em caso de dúvida, planeje o processamento de mensagens para que ele seja executado por muito tempo se houver dependências assíncronas, como acesso à rede ou requisitos de carregamento de dados intensivos.

Editar o manifesto do app

Para usar FirebaseMessagingService, adicione o seguinte ao manifesto do seu app:

<service
    android:name=".java.MyFirebaseMessagingService"
    android:exported="false">
    <intent-filter>
        <action android:name="com.google.firebase.MESSAGING_EVENT" />
    </intent-filter>
</service>

Além disso, recomendamos que você defina valores padrão para personalizar a aparência das notificações. É possível especificar uma cor e um ícone padrão personalizados que serão aplicados sempre que valores equivalentes não estiverem definidos no payload da notificação.

Adicione estas linhas na tag application para definir a cor e o ícone padrão personalizados:

<!-- Set custom default icon. This is used when no icon is set for incoming notification messages.
     See README(https://goo.gl/l4GJaQ) for more. -->
<meta-data
    android:name="com.google.firebase.messaging.default_notification_icon"
    android:resource="@drawable/ic_stat_ic_notification" />
<!-- Set color used with incoming notification messages. This is used when no color is set for the incoming
     notification message. See README(https://goo.gl/6BKBk7) for more. -->
<meta-data
    android:name="com.google.firebase.messaging.default_notification_color"
    android:resource="@color/colorAccent" />

O Android exibe e usa o ícone personalizado padrão para:

  • todas as mensagens de notificação enviadas pelo Editor do Notificações;
  • todas as mensagens de notificação que não definem explicitamente o ícone no payload da notificação.

Se não houver um ícone personalizado padrão ou nenhum ícone estiver definido no payload de notificação, o Android exibirá o ícone do aplicativo renderizado na cor branca.

Substituir onMessageReceived

Ao modificar o método FirebaseMessagingService.onMessageReceived, é possível realizar ações com base no objeto RemoteMessage recebido e acessar os dados da mensagem:

Kotlin

override fun onMessageReceived(remoteMessage: RemoteMessage) {
    // TODO(developer): Handle FCM messages here.
    // Not getting messages here? See why this may be: https://goo.gl/39bRNJ
    Log.d(TAG, "From: ${remoteMessage.from}")

    // Check if message contains a data payload.
    if (remoteMessage.data.isNotEmpty()) {
        Log.d(TAG, "Message data payload: ${remoteMessage.data}")

        // Check if data needs to be processed by long running job
        if (needsToBeScheduled()) {
            // For long-running tasks (10 seconds or more) use WorkManager.
            scheduleJob()
        } else {
            // Handle message within 10 seconds
            handleNow()
        }
    }

    // Check if message contains a notification payload.
    remoteMessage.notification?.let {
        Log.d(TAG, "Message Notification Body: ${it.body}")
    }

    // Also if you intend on generating your own notifications as a result of a received FCM
    // message, here is where that should be initiated. See sendNotification method below.
}

Java

@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
    // TODO(developer): Handle FCM messages here.
    // Not getting messages here? See why this may be: https://goo.gl/39bRNJ
    Log.d(TAG, "From: " + remoteMessage.getFrom());

    // Check if message contains a data payload.
    if (remoteMessage.getData().size() > 0) {
        Log.d(TAG, "Message data payload: " + remoteMessage.getData());

        if (/* Check if data needs to be processed by long running job */ true) {
            // For long-running tasks (10 seconds or more) use WorkManager.
            scheduleJob();
        } else {
            // Handle message within 10 seconds
            handleNow();
        }

    }

    // Check if message contains a notification payload.
    if (remoteMessage.getNotification() != null) {
        Log.d(TAG, "Message Notification Body: " + remoteMessage.getNotification().getBody());
    }

    // Also if you intend on generating your own notifications as a result of a received FCM
    // message, here is where that should be initiated. See sendNotification method below.
}

Manter o dispositivo ativado ao processar mensagens do FCM

Se o app precisar manter o dispositivo ativo enquanto processa uma mensagem do FCM, ele precisará manter um WakeLock durante esse período ou criar um job do WorkManager. Os WakeLocks funcionam bem para atividades de processamento curtas que podem exceder os tempos limite padrão do onMessageReceived. Para fluxos de trabalho estendidos, como o envio de vários RPCs seriais aos servidores, é mais adequado usar um trabalho do WorkManager do que um WakeLock. Nesta seção, vamos focar em como usar WakeLocks. Um WakeLock impede que o dispositivo entre em suspensão enquanto o app está em execução, o que pode resultar em maior uso da bateria. Portanto, o uso de WakeLocks deve ser reservado para casos em que o app não pode ser pausado ao processar a mensagem, como:

  • Notificações urgentes para o usuário.
  • Interações com algo fora do dispositivo que não deve ser interrompido, como transferências de rede ou comunicações com outro dispositivo, como um smartwatch pareado.

Primeiro, verifique se o app solicita a permissão WakeLock. O SDK do FCM inclui essa permissão por padrão, então normalmente não é preciso adicionar nada.

<uses-permission android:name="android.permission.WAKE_LOCK" />

Em seguida, seu app precisará adquirir um WakeLock no início do callback FirebaseMessagingService.onMessageReceived() e liberá-lo no final.

FirebaseMessagingService personalizado do app:

@Override
public void onMessageReceived(final RemoteMessage message) {
  // If this is a message that is time sensitive or shouldn't be interrupted
  WakeLock wakeLock = getSystemService(PowerManager.class).newWakeLock(PARTIAL_WAKE_LOCK, "myApp:messageReceived");
  try {
    wakeLock.acquire(TIMEOUT_MS);
    // handle message
    ...
  finally {
    wakeLock.release();
  }
}

Substituir onDeletedMessages

Em algumas situações, é possível que o FCM não entregue uma mensagem. Isso ocorre quando há muitas mensagens pendentes (mais de 100) no app em um dispositivo específico no momento em que ele é conectado ou se o dispositivo não for conectado ao FCM por mais de um mês. Nesses casos, você pode receber um callback para FirebaseMessagingService.onDeletedMessages(). Ao receber esse retorno de chamada, a instância do app precisa executar uma sincronização completa com seu servidor do app. Se você não enviou uma mensagem ao app no dispositivo nas últimas quatro semanas, o FCM não vai chamar o onDeletedMessages().

Processar mensagens de notificação em um app em segundo plano

Quando seu app está em segundo plano, o Android direciona as notificações para a bandeja do sistema. Quando um usuário toca nelas, a tela de início do aplicativo é aberta por padrão.

Isso inclui mensagens que contêm payload de notificação e dados, assim como todas aquelas enviadas pelo console do Notificações. Nesses casos, a notificação é entregue à bandeja do sistema do dispositivo e o payload de dados é entregue nos extras da intent da atividade da tela de início.

Confira as informações sobre a entrega de mensagens ao seu app no painel de relatórios do FCM, que registra o número de mensagens enviadas e abertas em dispositivos Apple e Android, além de dados de "impressões" (notificações vistas pelos usuários) para apps Android.