Android avançado no Kotlin 01.2: Firebase Cloud Messaging no Android

Este codelab faz parte do curso Android avançado no Kotlin. Você vai aproveitar mais este curso se fizer os codelabs em sequência, mas isso não é obrigatório. Todos os codelabs do curso estão listados na página de destino dos codelabs do Android avançado em Kotlin.

Introdução

No codelab anterior, você adicionou notificações ao seu cronômetro de ovos, que são criadas e acionadas no app. Outro caso de uso importante das notificações é o envio remoto de notificações push, que podem ser recebidas mesmo quando o app não está em execução.

O que é uma notificação push?

As notificações push são aquelas que o servidor "envia" para dispositivos móveis. Elas podem ser enviadas para um dispositivo, independentemente de o app estar em execução ou não.

As notificações push são uma ótima maneira de informar os usuários sobre uma atualização ou lembrar de uma tarefa ou recurso. Imagine esperar que um produto volte ao estoque. Com uma notificação push, um app de compras pode informar sobre atualizações de estoque em vez de você ter que verificar o status todos os dias.

As notificações push usam o padrão publicar/inscrever-se, que permite que apps de back-end enviem conteúdo relevante para clientes interessados. Sem um modelo de publicação/assinatura, os usuários do app precisariam verificar periodicamente se há atualizações. Esse processo é cansativo e não confiável para os usuários. Além disso, à medida que o número de clientes aumenta, essas verificações periódicas colocam uma carga muito grande nos recursos de rede e processamento, tanto para o servidor do app quanto para o dispositivo do usuário.

Assim como todos os outros tipos de notificações, respeite seus usuários com notificações push. Se o conteúdo da notificação não for interessante ou relevante para o usuário, ele poderá desativar todas as notificações do seu app.

O que é o Firebase Cloud Messaging?

O Firebase Cloud Messaging faz parte da plataforma Firebase para desenvolvimento de dispositivos móveis. Normalmente, você precisaria configurar um servidor do zero que pudesse se comunicar com dispositivos móveis para acionar notificações. Com o Firebase Cloud Messaging, é possível enviar notificações para todos os usuários do app instalado ou para um subconjunto deles sem configurar um servidor. Por exemplo, você pode enviar um lembrete ou uma promoção especial, como um presente grátis.É possível enviar uma notificação remotamente para um ou vários dispositivos.

Você também pode usar o Firebase Cloud Messaging para transferir dados do seu app de back-end ou de um projeto do Firebase para os usuários.

Neste codelab, você vai aprender a usar o Firebase Cloud Messaging para enviar notificações push e dados para seu app Android.

Se você encontrar algum problema (bugs no código, erros gramaticais, instruções pouco claras, etc.) neste codelab, informe o problema no link Informar um erro no canto inferior esquerdo do codelab.

O que você já precisa saber

Você precisa:

  • Como criar apps Android em Kotlin. Em especial, trabalhe com o SDK do Android.
  • Como projetar seu app usando componentes de arquitetura e vinculação de dados.
  • Conhecimentos básicos sobre broadcast receivers.
  • Conhecimentos básicos sobre o AlarmManager.
  • Como criar e enviar notificações usando o NotificationManager.

O que você vai aprender

  • Como enviar mensagens push para o usuário pelo Firebase Cloud Messaging.
  • Como enviar dados de um back-end para seu app usando mensagens de dados, que fazem parte do Firebase Cloud Messaging.

Atividades deste laboratório

  • Adicione notificações push ao app inicial.
  • Processar o Firebase Cloud Messaging enquanto o app está em execução.
  • Transferir dados com o Firebase Cloud Messaging.

Neste codelab, você vai trabalhar com o código do codelab anterior sobre como usar notificações em apps Android. No codelab anterior, você criou um app de timer de ovo que envia notificações quando o tempo de cozimento termina. Neste codelab, você vai adicionar o Firebase Cloud Messaging para enviar notificações push aos usuários do app e lembrar que eles precisam comer ovos.

Para fazer o download do app de exemplo, você pode:

Clone o repositório do GitHub e mude para a ramificação starter:

$  git clone https://github.com/googlecodelabs/android-kotlin-notifications-fcm


Se preferir, você pode fazer o download do repositório como um arquivo ZIP, descompactar e abrir no Android Studio.

Fazer o download do ZIP

Etapa 1: criar um projeto do Firebase

Antes de adicionar o Firebase ao seu app Android, é preciso criar um projeto do Firebase para conectar ao app.

  1. Faça login no Console do Firebase.
  2. Clique em Adicionar projeto e selecione ou insira o Nome do projeto. Nomeie o projeto como fcm-codelab.
  3. Clique em Continuar.
  4. Você pode pular a configuração do Google Analytics desativando o botão Ativar o Google Analytics para este projeto .
  5. Clique em Criar projeto para concluir a configuração do projeto do Firebase.

Etapa 2: registrar seu app com o Firebase

Agora que você tem um projeto do Firebase, é possível adicionar seu app Android a ele.

  1. No centro da página de visão geral do projeto do Console do Firebase, clique no ícone do Android para iniciar o fluxo de trabalho de configuração.

  1. No campo Nome do pacote do Android, insira com.example.android.eggtimernotifications.
  2. Clique em Registrar app.

Importante : insira o ID correto do app, porque não é possível adicionar ou modificar esse valor depois de registrar o app no projeto do Firebase.

Etapa 3: adicionar o arquivo de configuração do Firebase ao seu projeto

Adicione o arquivo de configuração do Firebase para Android ao app.

  1. Clique em Fazer o download do google-services.json para receber o arquivo de configuração do Firebase para Android (google-services.json). Verifique se o arquivo de configuração não tem caracteres adicionais e se o nome é exatamente google-services.json.
  2. Mova o arquivo de configuração para o diretório do módulo (nível do app) do seu app.

Etapa 4: configurar seu projeto Android para ativar os produtos do Firebase

Para ativar os produtos do Firebase no seu app, adicione o plug-in google-services aos seus arquivos do Gradle.

  1. No arquivo Gradle no nível raiz (para envolvidos no projeto) (build.gradle), verifique se você tem o repositório Maven do Google.
  2. Em seguida, adicione regras para incluir o plug-in do Google Services.

build.gradle

buildscript {

  repositories {
    // Check that you have the following line (if not, add it):
    google()  // Google's Maven repository
  }

  dependencies {
    // ...

    // Add the following line:
    classpath 'com.google.gms:google-services:4.3.2'  // Google Services plugin
  }
}

allprojects {
  // ...

  repositories {
    // Check that you have the following line (if not, add it):
    google()  // Google's Maven repository
    // ...
  }
}
  1. No arquivo Gradle do módulo (nível do app), que geralmente é app/build.gradle, adicione uma linha para aplicar o plug-in na parte de baixo do arquivo.

app/build.gradle

apply plugin: 'com.android.application'

android {
  // ...
}

// Add the following line to the bottom of the file:
apply plugin: 'com.google.gms.google-services'  // Google Play services Gradle plugin

Nesta tarefa, você vai adicionar o Firebase Cloud Messaging (FCM) ao projeto para usar notificações push.

O código do serviço Android para o FCM deste codelab está em MyFirebaseMessagingService.kt. Nas etapas a seguir, você vai adicionar código ao seu app Android.

Você vai usar o Editor do Notificações para testar sua implementação. O Editor do Notificações é uma ferramenta que ajuda você a criar e enviar mensagens do site do console do Firebase.

  1. Abrir MyFirebaseMessagingService.kt
  2. Inspecione o arquivo e, em particular, as seguintes funções:
  • onNewToken(): chamado automaticamente se o serviço estiver registrado no manifesto do Android. Essa função é chamada quando você executa o app pela primeira vez e sempre que o Firebase emite um novo token para ele. Um token é uma chave de acesso ao projeto de back-end do Firebase. Ele é gerado para seu dispositivo cliente específico. Com esse token, o Firebase sabe para qual cliente o back-end precisa enviar mensagens. O Firebase também sabe se esse cliente é válido e tem acesso ao projeto do Firebase.
  • onMessageReceived: é chamada quando o app está em execução e o Firebase envia uma mensagem para ele. Essa função recebe um objeto RemoteMessage, que pode conter uma notificação ou uma mensagem de dados. Você vai aprender mais sobre as diferenças entre notificações e payloads de mensagens de dados mais adiante neste codelab.

Etapa 1: enviar notificações do FCM para um único dispositivo

O console de notificações permite testar o envio de uma notificação. Para enviar uma mensagem a um dispositivo específico usando o console, é necessário saber o token de registro dele.

Quando o back-end do Firebase gera um token novo ou atualizado, a função onNewToken() é chamada, com o novo token transmitido como um argumento. Se você quiser segmentar um único dispositivo ou criar um grupo de dispositivos para enviar uma mensagem de transmissão, acesse esse token estendendo FirebaseMessagingService e substituindo onNewToken().

  1. Abra AndroidManifest.xml e remova o comentário do seguinte código para ativar o MyFirebaseMessagingService no app de contagem regressiva de ovos. Os metadados de serviço no manifesto do Android registram o MyFirebaseMessagingService como um serviço e adicionam um filtro de intent para que esse serviço receba mensagens enviadas pelo FCM. A última parte dos metadados declara breakfast_notification_channel_id como default_notification_channel_id para o Firebase. Você vai usar esse ID na próxima etapa.
<!-- AndroidManifest.xml -->
<!-- TODO: Step 3.0 uncomment to start the service  -->

        <service
                android:name=".MyFirebaseMessagingService"
                android:exported="false">
            <intent-filter>
                <action android:name="com.google.firebase.MESSAGING_EVENT"/>
            </intent-filter>
        </service>
        <!-- [START fcm_default_icon] -->
        <!--
 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/common_google_signin_btn_icon_dark"/>
        <!--
 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"/> <!-- [END fcm_default_icon] -->
        <!-- [START fcm_default_channel] -->
        <meta-data
            android:name="com.google.firebase.messaging.default_notification_channel_id"
            android:value="@string/breakfast_notification_channel_id" />
        <!-- [END fcm_default_channel] -->

É uma boa ideia criar um novo canal de notificação para o FCM, já que os usuários podem querer ativar/desativar o cronômetro de ovos ou as notificações push do FCM separadamente.

  1. Abra ui/EggTimerFragment.kt . Em onCreateView(), adicione o seguinte código de criação de canal.
// EggTimerFragment.kt

   // TODO: Step 3.1 create a new channel for FCM
    createChannel(
        getString(R.string.breakfast_notification_channel_id),
        getString(R.string.breakfast_notification_channel_name)
    )
  1. Abra MyFirebaseMessagingService.kt e remova a marca de comentário da função onNewToken(). Essa função será chamada quando um novo token for gerado.
// MyFirebaseMessagingService.kt

   // TODO: Step 3.2 log registration token
    // [START on_new_token]
    /**
     * Called if InstanceID token is updated. This may occur if the security of
     * the previous token had been compromised. Note that this is called when the     
     * InstanceID 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
        // Instance ID token to your app server.
        sendRegistrationToServer(token)
    }
    // [END on_new_token]
  1. Execute o app de timer de ovo.
  2. Observe o logcat (View > Tool Windows > Logcat). Você vai ver uma linha de registro mostrando seu token, semelhante à abaixo. Esse é o token necessário para enviar uma mensagem ao dispositivo. Essa função só é chamada quando um novo token é criado.
2019-07-23 13:09:15.243 2312-2459/com.example.android.eggtimernotifications D/MyFirebaseMsgService: Refreshed token: f2esflBoQbI:APA91bFMzNNFaIskjr6KIV4zKjnPA4hxekmrtbrtba2aDbh593WQnm11ed54Mv6MZ9Yeerver7pzgwfKx7R9BHFffLBItLEgPvrtF0TtX9ToCrXZ5y7Hd-m

Observação : se o token não aparecer nas mensagens do logcat, talvez seu app já o tenha recebido antes. Nesse caso, desinstalar o app vai ajudar você a receber um novo token.

Agora você pode testar enviando uma notificação. Para enviar uma notificação, use o Editor do Notificações.

  1. Abra o console do Firebase e selecione seu projeto.
  2. Em seguida, selecione Cloud Messaging na navegação à esquerda.
  3. Clique em Enviar sua primeira mensagem.

  1. Insira Time for Breakfast! como título e Don't forget to eat eggs! como texto da notificação e selecione Enviar mensagem de teste. A caixa de diálogo pop-up Test on device aparece, pedindo que você forneça um token de registro do FCM.

  1. Copie o token do app do logcat.

  1. Cole esse token no campo Adicionar um token de registro do FCM na janela pop-up e clique no botão Adicionar ao lado do token.
  2. Na lista de caixas de seleção que aparece, selecione o token. O botão Testar vai ficar ativado.

  1. No dispositivo, coloque o app Egg Timer em segundo plano .
  2. No pop-up, clique em Testar.
  1. Depois de clicar em Testar, o dispositivo cliente de destino que tem seu app em segundo plano vai receber a notificação na bandeja de notificações do sistema. Você vai saber mais sobre como processar as mensagens do FCM quando o app estiver em primeiro plano mais tarde.

Tarefa: enviar notificações do FCM para um tópico

As mensagens de tópico do FCM são baseadas no modelo de publicação/assinatura.

Um app de mensagens é um bom exemplo do modelo publicar/inscrever. Imagine que um app verifica novas mensagens a cada 10 segundos. Isso não apenas vai descarregar a bateria do smartphone, mas também vai usar recursos de rede desnecessários e criar uma carga desnecessária no servidor do app. Em vez disso, um dispositivo cliente pode se inscrever e receber notificações quando houver novas mensagens entregues pelo seu app.

Com os tópicos, é possível enviar uma mensagem para vários dispositivos que optaram por receber mensagens sobre um tópico específico. Para os clientes, os temas são fontes de dados específicas de interesse. Para o servidor, os tópicos são grupos de dispositivos que ativaram o recebimento de atualizações sobre uma fonte de dados específica. Os tópicos podem ser usados para apresentar categorias de notificações, como notícias, previsões do tempo e resultados esportivos. Nesta parte do codelab, você vai criar um tópico "café da manhã" para lembrar os usuários interessados do app de comer ovos no café da manhã.

Para se inscrever em um tópico, o app cliente chama a função subscribeToTopic() do Firebase Cloud Messaging com o nome do tópico breakfast. Essa chamada pode ter dois resultados. Se o caller for bem-sucedido, o callback OnCompleteListener será chamado com a mensagem inscrita. Se o cliente não conseguir fazer a inscrição, o callback vai receber uma mensagem de erro.

No seu app, você vai inscrever automaticamente os usuários no tópico "café da manhã". No entanto, na maioria dos apps de produção, é melhor dar aos usuários o controle sobre quais tópicos assinar.

  1. Abra EggTimerFragment.kt e encontre a função subscribeTopic() vazia.
  2. Receba uma instância de FirebaseMessaging e chame a função subscibeToTopic() com o nome do tópico.
  3. Adicione um addOnCompleteListener para receber uma notificação do FCM sobre o sucesso ou a falha da sua inscrição.
// EggTimerFragment.kt

   // TODO: Step 3.3 subscribe to breakfast topic
    private fun subscribeTopic() {
        // [START subscribe_topics]
        FirebaseMessaging.getInstance().subscribeToTopic(TOPIC)
            .addOnCompleteListener { task ->
                var msg = getString(R.string.message_subscribed)
                if (!task.isSuccessful) {
                    msg = getString(R.string.message_subscribe_failed)
                }
                Toast.makeText(context, msg, Toast.LENGTH_SHORT).show()
            }
        // [END subscribe_topics]
    }
  1. Chame a função subscribeTopic() para se inscrever em um tópico quando o app for iniciado. Role para cima até onCreateView() e adicione uma chamada para subscribeTopic().
// EggTimerFragment.kt

   // TODO: Step 3.4 call subscribe topics on start
    subscribeTopic()

    return binding.root
  1. Para se inscrever no tópico do café da manhã, execute o app novamente. Você vai ver uma mensagem curta dizendo "Inscrito no tópico".

Agora você pode testar o envio de mensagens para um tópico:

  1. Abra o Editor do Notificações e selecione Criar notificação.
  2. Defina o Título da notificação e o Texto da notificação como antes.
  3. Desta vez, em vez de enviar a mensagem para um único dispositivo, clique em Tópico em Destino e insira breakfast como o tópico da mensagem.

  1. Selecione Agora para agendar.

  1. Verifique se o app está em execução em segundo plano no dispositivo de teste.
  1. Clique em Revisar e em Publicar. Se você puder executar o app em mais de um dispositivo, teste e observe se a notificação é recebida em todos os dispositivos inscritos nesse tópico.

O app agora tem os seguintes canais para notificações: Egg e Breakfast. Em um dispositivo cliente, toque no ícone do app e o mantenha pressionado, selecione Informações e clique em Notificações. Você vai ver os canais de notificação Ovo e Café da manhã, conforme mostrado na captura de tela a seguir. Se você desmarcar o canal Café da manhã, o app não vai receber notificações enviadas por ele.

Ao usar notificações, sempre tenha em mente que os usuários podem desativar qualquer canal de notificação a qualquer momento.

Etapa 1: mensagens de dados

As mensagens do FCM também podem conter um payload de dados que processa as mensagens no app cliente. Use mensagens de dados em vez de mensagens de notificação.

Para processar mensagens de dados, você precisa processar o payload de dados na função onMessageReceived() de MyFirebaseMessagingService. O payload é armazenado na propriedade data do objeto remoteMessage. O objeto remoteMessage e a propriedade data podem ser null.

  1. Abrir MyFirebaseMessagingService.
  2. Verifique se a propriedade data do objeto remoteMessage tem algum valor e imprima os dados no registro.
// MyFirebaseMessagingService.kt

    // [START receive_message]
    override fun onMessageReceived(remoteMessage: RemoteMessage?) {
        // Not getting messages here? See why this may be: https://goo.gl/39bRNJ
        Log.d(TAG, "From: ${remoteMessage?.from}")
        
       // TODO: Step 3.5 check messages for data
        // Check if the message contains a data payload.
        remoteMessage?.data?.let {
            Log.d(TAG, "Message data payload: " + remoteMessage.data)
        }

    }
    // [END receive_message]

Para testar seu código, use o Editor do Notificações novamente.

  1. Abra o editor de notificações, crie uma mensagem e defina o Destino como o tópico "café da manhã".
  2. Desta vez, quando chegar à etapa 4, Outras opções, defina as propriedades de chave e valor de Dados personalizados da seguinte maneira:
  1. Chave: eggs
  2. Valor : 3

  1. Verifique se o app está em execução em primeiro plano. Se o app estiver em segundo plano, a mensagem do FCM vai acionar uma notificação automática, e a função onMessageReceived() vai receber apenas o objeto remoteMessage quando o usuário clicar na notificação.
  2. Envie a mensagem do Editor de Notificações e observe o registro de mensagens de dados que aparece no logcat.

Etapa 2: processar mensagens em primeiro e segundo plano

Quando um dispositivo cliente que executa seu app recebe uma mensagem com payloads de notificação e dados, o comportamento do app depende de ele estar em primeiro ou segundo plano no dispositivo:

  • Se o app estiver em segundo plano e a mensagem tiver um payload de notificação, ela será mostrada automaticamente na bandeja de notificações. Se a mensagem também tiver um payload de dados, ele será processado pelo app quando o usuário tocar na notificação.
  • Se o app estiver em execução em primeiro plano e a notificação de mensagem tiver um payload de notificação, ela não vai aparecer automaticamente. O app precisa decidir como processar a notificação na função onMessageReceived(). Se a mensagem também tiver um payload de dados, os dois payloads serão processados pelo app.

Para este codelab, você quer lembrar o usuário do app de comer ovos no café da manhã. Você não planeja enviar dados, mas quer garantir que a notificação de lembrete sempre apareça, independente de o app estar em primeiro ou segundo plano.

Quando você envia uma mensagem do FCM para dispositivos em que o app de contagem regressiva está instalado, a mensagem de notificação é mostrada automaticamente se o app não estiver em execução ou estiver em segundo plano. No entanto, se o app estiver sendo executado em primeiro plano, a notificação não será mostrada automaticamente. Em vez disso, o código do app decide o que fazer com a mensagem. Se o app estiver em primeiro plano ao receber uma mensagem do FCM, a função onMessageReceived() será acionada automaticamente com a mensagem do FCM. É aqui que seu app pode processar silenciosamente notificações e payloads de dados ou acionar uma notificação.

Para seu app, você quer garantir que o usuário receba o lembrete quando o app estiver em primeiro plano. Então, vamos implementar um código para acionar uma notificação:

  1. Abra a função onMessageReceived() em MyFirebaseMessagingService novamente.
  2. Imediatamente após o código que você adicionou recentemente para verificar a mensagem de dados, adicione o código a seguir, que envia uma notificação usando a estrutura de notificações.
// MyFirebaseMessagingService.kt

    // TODO: Step 3.6 check messages for notification and call sendNotification
    // Check if the message contains a notification payload.
    remoteMessage.notification?.let {
        Log.d(TAG, "Message Notification Body: ${it.body}")
        sendNotification(it.body as String)
    }
  1. Se você executar o app novamente e enviar uma notificação usando o compositor de notificações, vai aparecer uma notificação como a que você via na primeira parte do codelab, independente de o app estar em primeiro ou segundo plano.

O código da solução está na ramificação principal do código baixado.

Curso da Udacity:

Documentação do Firebase:

Para acessar links de outros codelabs deste curso, consulte a página inicial dos codelabs do curso Android avançado no Kotlin.