Criar um app para Android usando o Firebase e o ambiente flexível do App Engine

Este tutorial demonstra como usar o Firebase para escrever um aplicativo para dispositivos móveis com armazenamento de dados de back-end, sincronização em tempo real e registro de eventos de usuário. Os servlets Java em execução no ambiente flexível do App Engine do Google Cloud Platform (GCP) rastreiam e processam novos registros de usuários armazenados no Firebase.

Nas instruções, veja como fazer isso usando o Firebase e o ambiente flexível do App Engine.

Para seu aplicativo processar dados do usuário ou orquestrar eventos, estenda o Firebase com o ambiente flexível do App Engine para realizar a sincronização automática de dados em tempo real.

Com o aplicativo de amostra Playchat, as mensagens de bate-papo são armazenadas no Firebase Realtime Database, onde esses dados são sincronizados automaticamente com os dispositivos. Nesse aplicativo, os logs de eventos do usuário também são gravados no Firebase. Para saber mais sobre como o banco de dados sincroniza dados, consulte Como funciona? na documentação do Firebase.

Veja no diagrama abaixo a arquitetura do cliente do Playchat.

Arquitetura do cliente do Playchat

Um conjunto de servlets Java em execução no ambiente flexível do App Engine é registrado como listener no Firebase. Eles respondem aos logs de evento do novo usuário e processam os dados desses logs. As transações são usadas para garantir que somente um servlet processe cada log.

Veja no diagrama abaixo a arquitetura do servidor do Playchat.

Arquitetura do servidor do Playchat

A comunicação entre o aplicativo e o servlet acontece em três partes:

  • Quando um novo usuário faz login no Playchat, um servlet de geração de registros é solicitado pelo aplicativo para esse usuário. Isso é feito incluindo uma entrada em /inbox/ no Firebase Realtime Database.

  • Um dos servlets aceita a atribuição por meio da atualização do valor da entrada para seu identificador de servlet. Uma transação do Firebase é usada para garantir que ele seja o único a atualizar o valor. Depois disso, a solicitação é ignorada por todos os outros servlets.

  • Quando o usuário faz login, sai ou muda para um novo canal, a ação é registrada pelo Playchat no /inbox/[SERVLET_ID]/[USER_ID]/, em que [SERVLET_ID] é o identificador da instância do servlet e [USER_ID] é o valor de hash que representa o usuário.

  • A inclusão de novas entradas na caixa de entrada é monitorada e os dados do log são coletados pelo servlet.

Neste aplicativo de amostra, os dados do log são copiados localmente e exibidos em uma página da Web pelos servlets. Em uma versão de produção deste aplicativo, os dados de registro são processados pelos servlets ou copiados para o Cloud Storage, Cloud Bigtable ou BigQuery para armazenamento e análise.

Objetivos

Neste tutorial, confira como:

  • criar um app para Android, Playchat, que armazene dados no Firebase Realtime Database;

  • executar um servlet Java nos ambientes flexíveis do App Engine que se conecte ao Firebase e receba notificações quando os dados armazenados no Firebase forem alterados;

  • usar esses dois componentes para criar um serviço de back-end de streaming distribuído para coletar e processar dados do registro.

Custos

O Firebase tem um nível de uso gratuito. Quando o uso desses serviços é menor do que os limites especificados no plano gratuito do Firebase, não há custos para isso.

As instâncias do ambiente flexível do App Engine são cobradas pelo custo das máquinas virtuais subjacentes do Google Compute Engine.

Antes de começar

Instale os seguintes softwares:

Instale o componente Java do App Engine do Cloud SDK executando o comando a seguir na linha de comando.

gcloud components install app-engine-java

Como clonar o código da amostra

  1. Clone o código do aplicativo cliente de front end.

    git clone https://github.com/GoogleCloudPlatform/firebase-android-client
    
  2. Clone o código do servlet de back-end.

    git clone https://github.com/GoogleCloudPlatform/firebase-appengine-backend
    

Como gerar uma impressão digital SHA-1 para o aplicativo

Para autenticar o aplicativo cliente para Login do Google, informe uma impressão digital SHA-1 do certificado. Este tutorial usa o keystore de depuração. Para mais informações sobre como criar versões da impressão digital do keystore, veja Como autenticar seu cliente.

  • Crie um SHA-1 do keystore de depuração.

    keytool -exportcert -list -v \
    -alias androiddebugkey -keystore ~/.android/debug.keystore
    

Como criar um projeto do Firebase

  1. Crie uma conta do Firebase ou faça login em uma conta.

  2. Clique em Adicionar projeto.

  3. Em Nome do projeto, insira: Playchat.

  4. Siga as etapas de configuração restantes e clique em Criar projeto.

  5. Após o assistente provisionar seu projeto, clique em Continuar.

  6. Na página Visão geral do projeto, clique na engrenagem Configurações e depois em Configurações do projeto.

  7. Clique em Adicionar o Firebase a um app para Android.

  8. Em Nome do pacote Android, insira: com.google.cloud.solutions.flexenv

  9. Em Certificado de assinatura de depuração SHA-1, insira o valor SHA-1 gerado na seção anterior.

  10. Clique em Registrar app.

  11. Siga os passos da seção Fazer download do arquivo de configuração para adicionar o arquivo google-services.json ao projeto.

  12. Clique em Avançar na seção Fazer download do arquivo de configuração.

  13. Anote as alterações sugeridas para os arquivos build.gradle em nível de projeto e de aplicativo.

  14. Clique em Avançar na seção Adicionar SDK do Firebase.

  15. Clique em Ignorar esta etapa na seção Executar seu aplicativo para verificar a instalação.

Como criar um banco de dados em tempo real

  1. No menu à esquerda do Console do Firebase, selecione Banco de dados no grupo Develop.

  2. Na página Banco de dados, vá para a seção Realtime Database e clique em Criar banco de dados.

  3. Na caixa de diálogo Regras de segurança para Realtime Database, selecione Iniciar no modo de teste e clique em Ativar.

    Isso exibe os dados armazenados no Firebase. Nas etapas seguintes deste tutorial, revisite essa página da Web para ver os dados acrescentados e atualizados pelo aplicativo cliente e pelo servlet do back-end.

  4. Anote o URL do Firebase para seu projeto. Ele está no formato https://[FIREBASE_PROJECT_ID].firebaseio.com/ e aparece ao lado de um ícone de link.

Como ativar a autenticação do Google para o projeto do Firebase

Há vários provedores de login que podem ser configurados para fazer a conexão com o projeto do Firebase. Neste tutorial, você aprende a configurar a autenticação para que o usuário faça o login usando uma Conta do Google.

  1. No menu à esquerda do Console do Firebase, selecione Autenticação no grupo Develop.

  2. Clique em Configurar método de login.

  3. Selecione Google, alterne para Ativar e clique em Salvar.

Como adicionar uma conta de serviço ao projeto do Firebase

O login no servlet de back-end não é feito usando uma Conta do Google. Em vez disso, é usada uma conta de serviço para se conectar com o Firebase. Nas etapas a seguir, saiba como criar uma conta de serviço para conectar-se com o Firebase e adicionar as credenciais dessa conta ao código do servlet.

  1. No menu à esquerda do Console do Firebase, ao lado da página inicial do projeto do Playchat, selecione a engrenagem Configurações e Configurações do projeto.

  2. Selecione Contas de serviço e Gerenciar todas as contas de serviço.

  3. Clique em CRIAR CONTA DE SERVIÇO.

  4. Defina as configurações a seguir:

    1. Em Nome da conta de serviço, insira playchat-servlet.
    2. Em Papel, selecione Projeto > Proprietário.

    3. Marque Fornecer uma nova chave privada.

    4. Selecione JSON em Tipo de chave.
  5. Clique em Criar

  6. Faça o download do arquivo de chave JSON da conta de serviço e salve-o no projeto de serviço de back-end firebase-appengine-backend, no diretório src/main/webapp/WEB-INF/. O nome do arquivo está no formato Playchat-[UNIQUE_ID].json.

  7. Edite src/main/webapp/WEB-INF/web.xml e os parâmetros de inicialização da seguinte forma:

    • Substitua JSON_FILE_NAME pelo nome do arquivo de chave JSON do download realizado.

    • Substitua FIREBASE_URL pelo URL do Firebase anotado anteriormente.

      <init-param>
        <param-name>credential</param-name>
        <param-value>/WEB-INF/JSON_FILE_NAME</param-value>
      </init-param>
      <init-param>
        <param-name>databaseUrl</param-name>
        <param-value>FIREBASE_URL</param-value>
      </init-param>
      

Como ativar o faturamento e APIs para o projeto do Cloud Platform

Para que o serviço de back-end seja executado no Cloud Platform, você precisa ativar o faturamento e as APIs do projeto. O projeto do Cloud Platform é o mesmo criado na seção Criar um projeto do Firebase e tem o mesmo identificador.

  1. No Console do Cloud Platform, selecione o projeto do Playchat.

    Acessar a página "Projetos"

  2. Verifique se o faturamento foi ativado para o projeto.

    Saiba como ativar o faturamento

  3. Ativar App Engine Admin API e Compute Engine API APIs.

    Ativar a de APIs

Como criar e implantar o serviço de back-end

Uma configuração do Docker é usada no serviço de back-end desta amostra para especificar o ambiente de hospedagem. Essa personalização significa que você precisa usar o ambiente flexível do App Engine, não o ambiente padrão.

Para criar o servlet de back-end e implantá-lo no ambiente flexível do App Engine, use o plug-in Maven do Google App Engine. Esse plug-in já está especificado no arquivo de criação do Maven, incluído nesta amostra.

Como configurar o projeto

Para que o servlet de back-end seja criado corretamente pelo Maven, forneça a ele o projeto do Google Cloud Platform (GCP) para ativar os recursos desse servlet. O identificador de projeto do GCP e do Firebase são os mesmos.

  1. Forneça as credenciais usadas pela ferramenta gcloud para acessar o GCP.

    gcloud auth login
    
  2. Defina o projeto do Firebase com o comando a seguir, substituindo [FIREBASE_PROJECT_ID] pelo nome do código do projeto anotado anteriormente.

    gcloud config set project [FIREBASE_PROJECT_ID]
    
  3. Para verificar se o projeto foi definido, liste a configuração.

    gcloud config list
    

(Opcional) Como executar o serviço no servidor local

Ao desenvolver um novo serviço de back-end, execute o serviço localmente antes de implantá-lo no App Engine para repetir rapidamente as alterações sem a sobrecarga de uma implantação completa.

Quando o servidor é executado localmente, ele não usa uma configuração Docker nem é executado em um ambiente do App Engine. Em vez disso, o Maven garante que todas as bibliotecas dependentes sejam instaladas localmente e o aplicativo seja executado no servidor da Web Jetty.

  1. No diretório firebase-appengine-backend, crie e execute o módulo de back-end localmente com o comando a seguir:

    mvn clean package appengine:run
    
    mvn clean package appengine:run -Dgcloud.gcloud_directory=[PATH_TO_TOOL]
    
  2. Quando aparecer a pergunta Quer que o aplicativo "Python.app" aceite conexões de rede recebidas?, selecione Permitir.

Quando a implantação terminar, abra http://localhost:8080/printLogs para verificar se o serviço de back-end está sendo executado. Na página da Web será exibida Caixa de entrada:, seguida por um identificador de 16 dígitos. Esse é o identificador da caixa de entrada para o servlet em execução sendo executado na máquina local.

Conforme você atualiza a página, esse identificador permanece inalterado. Seu servidor local ativa uma única instância de servlet. Isso é útil para testes, porque há apenas um identificador de servlet armazenado no Firebase Realtime Database.

Para encerrar o servidor local, digite Ctrl + C.

Como implantar o serviço no ambiente flexível do App Engine

Quando você executa o serviço de back-end no ambiente flexível do App Engine, ele usa a configuração em /firebase-appengine-backend/src/main/webapp/Dockerfiles para criar o ambiente de hospedagem onde o serviço é executado. Nesse ambiente flexível, várias instâncias do servlet são geradas e dimensionadas para atender à demanda.

  • No diretório firebase-appengine-backend, crie e execute o módulo de back-end localmente com o comando a seguir:

    mvn clean package appengine:deploy
    
    mvn clean package appengine:deploy -Dgcloud.gcloud_directory=[PATH_TO_GCLOUD]
    

Durante a compilação, você vê as linhas “Enviando o contexto de compilação para o daemon do Docker…”. Use o comando anterior para fazer o upload da configuração do Docker e defini-la para o ambiente flexível do App Engine.

Ao final da implantação, abra https://[FIREBASE_PROJECT_ID].appspot.com/printLogs, em que [FIREBASE_PROJECT_ID] é o identificador em Criar um projeto do Firebase. Na página da Web será exibida Caixa de entrada:, seguida por um identificador de 16 dígitos. Esse é o identificador da caixa de entrada de um servlet em execução no ambiente flexível do App Engine.

Esse identificador muda periodicamente, à medida que a página é atualizada, já que o App Engine ativa diversas instâncias de servlet para processar as solicitações de clientes recebidas.

Como adicionar o Firebase e o Google Play Services ao app para Android

O aplicativo cliente usa o Firebase Realtime Database para armazenar e sincronizar mensagens e gravar logs de eventos de usuários. O aplicativo cliente usa o Google Play Services para autenticar usuários com a Conta do Google.

  1. No Android Studio, selecione Ferramentas > SDK Manager.

  2. Na parte superior do painel direito, selecione Ferramentas do SDK.

  3. Selecione o Google Play Services, caso ainda não o tenha feito.

  4. Clique em OK.

  5. Selecione Arquivo > Abrir… e escolha o diretório firebase-android-client.

  6. Aguarde a informação do projeto "Gradle" para terminar a criação. Caso seja solicitado a usar o "wrapper Gradle", clique em OK.

  7. As alterações aos arquivos build.gradle de projeto e aplicativo que você anotou em Criar um projeto do Firebase já foram feitas no código de amostra.

Como executar e testar o app para Android

  1. No Android Studio, com o projeto firebase-android-client em aberto, selecione Executar > Executar "aplicativo".

  2. Selecione um dispositivo ou emulador que execute o Android 6.0 com as APIs Google como seu dispositivo de teste.

  3. Quando o aplicativo for carregado no dispositivo, faça login com a Conta do Google.

    Fazer login no Playchat

  4. Clique no menu à esquerda do título PlayChat e selecione o canal de livros.

    Selecionar um canal

  5. Digite uma mensagem.

    Enviar uma mensagem

  6. Quando você faz isso, o aplicativo Playchat armazena a mensagem no Firebase Realtime Database. O Firebase sincroniza os dados armazenados no banco de dados em todos os dispositivos. Os dispositivos que executam o Playchat exibirão a nova mensagem quando um usuário selecionar o canal de livros.

    Enviar uma mensagem

Como verificar os dados

Depois de usar o aplicativo Playchat para gerar alguns eventos do usuário, verifique se os servlets estão sendo registrados como listeners e coletando logs de eventos de usuários.

Abra o Firebase Realtime Database do aplicativo, em que [FIREBASE_PROJECT_ID] é o identificador em Criar um projeto do Firebase.

https://console.firebase.google.com/project/[FIREBASE_PROJECT_ID]/database/data

Na parte inferior do Firebase Realtime Database, no local de dados /inbox/, há um grupo de nodes prefixados por client- e seguido por uma chave gerada aleatoriamente, representando o login da conta de um usuário. A última entrada neste exemplo, client-1240563753, é seguida por um identificador de 16 dígitos do servlet que atualmente atende eventos de registros desse usuário, neste exemplo 0035806813827987.

Dados armazenados no Firebase Realtime Database

Logo acima, no local de dados /inbox/, estão os identificadores de todos os servlets atribuídos no momento. Neste exemplo, os logs são coletados por apenas um servlet. Em /inbox/[SERVLET_IDENTIFIER] estão os logs de usuário gravados pelo aplicativo nesse servlet.

Abra a página do App Engine do serviço de back-end: https://[FIREBASE_PROJECT_ID].appspot.com/printLogs, em que [FIREBASE_PROJECT_ID] é o identificador em Como criar um projeto do Firebase. O identificador do servlet que registrou os eventos do usuário gerados por você é exibido na página. É possível também ver as entradas de registros para os eventos abaixo do identificador da caixa de entrada do servlet.

Como explorar o código

O app Playchat para Android define a classe FirebaseLogger para gravar os logs de eventos de usuários no Firebase Realtime Database.

import com.google.cloud.solutions.flexenv.common.LogEntry;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;

/*
 * FirebaseLogger pushes user event logs to a specified path.
 * A backend servlet instance listens to
 * the same key and keeps track of event logs.
 */
class FirebaseLogger {
    private final DatabaseReference logRef;

    FirebaseLogger(String path) {
        logRef = FirebaseDatabase.getInstance().getReference().child(path);
    }

    public void log(String tag, String message) {
        LogEntry entry = new LogEntry(tag, message);
        logRef.push().setValue(entry);
    }

}

Quando um novo usuário faz o login, o Playchat chama a função requestLogger para adicionar uma nova entrada ao local /inbox/ no Firebase Realtime Database e configurar um listener. Assim, o Playchat é capaz de responder quando um servlet atualiza o valor dessa entrada, aceitando a atribuição.

Quando um servlet atualiza o valor, o Playchat remove o listener e grava o registro como "Conectado" na caixa de entrada do servlet.

/*
 * Request that a servlet instance be assigned.
 */
private void requestLogger(final LoggerListener loggerListener) {
    final DatabaseReference databaseReference = FirebaseDatabase.getInstance().getReference();
    databaseReference.child(IBX + "/" + inbox).addListenerForSingleValueEvent(new ValueEventListener() {
        public void onDataChange(@NonNull DataSnapshot snapshot) {
            if (snapshot.exists() && snapshot.getValue(String.class) != null) {
                firebaseLoggerPath = IBX + "/" + snapshot.getValue(String.class) + "/logs";
                fbLog = new FirebaseLogger(firebaseLoggerPath);
                databaseReference.child(IBX + "/" + inbox).removeEventListener(this);
                loggerListener.onLoggerAssigned();
            }
        }

        public void onCancelled(@NonNull DatabaseError error) {
            Log.e(TAG, error.getDetails());
        }
    });

    databaseReference.child(REQLOG).push().setValue(inbox);
}

No serviço de back-end, quando uma instância de servlet é iniciada, a função init(ServletConfig config) em MessageProcessorServlet.java conecta-se ao Firebase Realtime Database e adiciona um listener no local de dados /inbox/.

Quando uma nova entrada é adicionada ao local de dados /inbox/, o valor é atualizado pelo servlet com o respectivo identificador. Isso indica ao aplicativo Playchat que a atribuição para processar registros desse usuário foi aceita no servlet. As transações do Firebase são usadas para garantir que somente um servlet possa atualizar o valor e aceitar a atribuição.

/*
 * Receive a request from a client and reply back its inbox ID.
 * Using a transaction ensures that only a single servlet instance replies
 * to the client. This lets the client know to which servlet instance
 * send consecutive user event logs.
 */
firebase.child(REQLOG).addChildEventListener(new ChildEventListener() {
  public void onChildAdded(DataSnapshot snapshot, String prevKey) {
    firebase.child(IBX + "/" + snapshot.getValue()).runTransaction(new Transaction.Handler() {
      public Transaction.Result doTransaction(MutableData currentData) {
        // Only the first servlet instance writes its ID to the client inbox.
        if (currentData.getValue() == null) {
          currentData.setValue(inbox);
        }
        return Transaction.success(currentData);
      }

      public void onComplete(DatabaseError error, boolean committed, DataSnapshot snapshot) {}
    });
    firebase.child(REQLOG).removeValue();
  }
  // ...
});

Depois de uma tarefa de processar os logs de eventos de um usuário ser aceita pelo servlet, um listener é adicionado. Esse listener detecta quando um novo arquivo de log é gravado na caixa de entrada pelo aplicativo Playchat. A resposta do servlet é recuperar os novos dados de log do Firebase Realtime Database.

/*
 * Initialize user event logger. This is just a sample implementation to
 * demonstrate receiving updates. A production version of this app should
 * transform, filter, or load to another data store such as Google BigQuery.
 */
private void initLogger() {
  String loggerKey = IBX + "/" + inbox + "/logs";
  purger.registerBranch(loggerKey);
  firebase.child(loggerKey).addChildEventListener(new ChildEventListener() {
    public void onChildAdded(DataSnapshot snapshot, String prevKey) {
      if (snapshot.exists()) {
        LogEntry entry = snapshot.getValue(LogEntry.class);
        logs.add(entry);
      }
    }

    public void onCancelled(DatabaseError error) {
      localLog.warning(error.getDetails());
    }

    public void onChildChanged(DataSnapshot arg0, String arg1) {}

    public void onChildMoved(DataSnapshot arg0, String arg1) {}

    public void onChildRemoved(DataSnapshot arg0) {}
  });
}

Como fazer a limpeza

Para evitar que os recursos usados nesse tutorial sejam cobrados na sua conta do Google Cloud Platform:

Excluir o projeto do Cloud Platform e do Firebase

A maneira mais simples de não ser cobrado é excluir o projeto criado neste tutorial. O projeto foi criado no Firebase console, mas também pode ser excluído no Console do Cloud Platform, uma vez que os dois correspondem ao mesmo projeto.

  1. No Console do GCP, acesse a página "Projetos".

    Acessar a página Projetos

  2. Na lista de projetos, selecione um e clique em Excluir projeto.
  3. Na caixa de diálogo, digite o código do projeto e clique em Encerrar para excluí-lo.

Excluir as versões não padrão do aplicativo App Engine

Se não quiser excluir o projeto do Cloud Platform e do Firebase, para reduzir os custos, exclua as versões não padrão do aplicativo de ambiente flexível do App Engine.

  1. No Console do GCP, acesse a página "Versões do App Engine".

    Acessar a página Versões

  2. Clique na caixa de seleção ao lado da versão do aplicativo não padrão que você quer excluir.
  3. Clique no botão Excluir na parte superior da página para excluir a versão do aplicativo.

Próximas etapas

  • Analisar e arquivar dados: neste exemplo, os dados do log são armazenados apenas na memória pelos servlets. Para ampliar esta amostra, use os servlets para arquivar, transformar e analisar os dados com serviços como Cloud Storage, Cloud Bigtable, Google Cloud Dataflow e BigQuery.

  • Distribuir uniformemente a carga de trabalho entre os servlets: no App Engine, você tem escalonamento automático e manual. O escalonamento automático detecta alterações no ambiente flexível, que responde adicionando ou removendo instâncias de VMs no cluster. Já com o escalonamento manual, você especifica um número estático de VMs para processar o tráfego. Para saber mais informações sobre como configurar o escalonamento, consulte Configurações de escalonamento de serviço na documentação do App Engine.

    Como os logs de atividade do usuário são atribuídos aos servlets por meio do acesso ao Firebase Realtime Database, a carga de trabalho pode não ser distribuída uniformemente. Por exemplo, um servlet pode processar mais logs de eventos de usuários do que outros.

    Para melhorar a eficiência, implemente um gerenciador para controlar de maneira independente a carga de trabalho de cada VM. Esse balanceamento de carga pode basear-se em métricas como, por exemplo, as solicitações de geração de logs por segundo ou o número de clientes simultâneos.

  • Recuperar logs de eventos de usuários não processados: nessa implementação de amostra, quando ocorre uma falha em uma instância de servlet, o aplicativo cliente associado a ela continua a enviar eventos de registros para a caixa de entrada do servlet no Firebase Realtime Database. Em uma versão de produção desse aplicativo, é preciso que o serviço de back-end identifique a situação para recuperar os logs de eventos de usuários não processados.

Esta página foi útil? Conte sua opinião sobre:

Enviar comentários sobre…