1. Boas-vindas
Este codelab prático faz parte da Unidade 1: Introdução ao curso Conceitos básicos para desenvolvedores Android (versão 2). Você aproveitará melhor este curso se trabalhar com os codelabs em sequência:
- Para ver a lista completa de codelabs do curso, consulte Codelabs para o curso Conceitos básicos para desenvolvedores Android (V2).
- Para mais detalhes sobre o curso, incluindo links para todos os capítulos do conceito, apps e slides, consulte Conceitos básicos para desenvolvedores Android (versão 2).
Introdução
Nesta seção, você aprenderá mais sobre o ciclo de vida da atividade. O ciclo de vida é o conjunto de estados em que uma atividade pode estar durante toda a vida útil, desde quando é criada até ser destruída e o sistema recuperar os recursos. À medida que o usuário navega entre as atividades no app (e dentro e fora dele), as atividades fazem a transição entre estados diferentes no ciclo de vida.
Cada estágio do ciclo de vida de uma atividade tem um método de callback correspondente: onCreate(), onStart(), onPause() e assim por diante. Quando uma atividade muda de estado, o método de callback associado é invocado. Você já viu um desses métodos: onCreate(). Ao substituir qualquer um dos métodos de callback do ciclo de vida nas classes Activity, é possível mudar o comportamento padrão da atividade em resposta a ações do usuário ou do sistema.
O estado da atividade também pode mudar em resposta a mudanças na configuração do dispositivo, por exemplo, quando o usuário gira o dispositivo de retrato para paisagem. Quando essas mudanças de configuração ocorrem, a atividade é destruída e recriada no estado padrão, e o usuário pode perder as informações inseridas na atividade. Para evitar confundir os usuários, é importante desenvolver o app para evitar a perda inesperada de dados. Mais adiante neste curso, você vai testar mudanças de configuração e aprender a preservar o estado de uma atividade em resposta a mudanças de configuração do dispositivo e outros eventos do ciclo de vida da atividade.
Você vai adicionar log statements ao app TwoActivities e observar as mudanças no ciclo de vida da atividade durante o uso do app. Depois, você vai trabalhar com essas mudanças e aprender como lidar com as entradas do usuário nessas condições.
Pré-requisitos
Você precisa saber:
- Criar e executar um projeto de app no Android Studio.
- Adicione log statements ao app e veja esses registros no painel Logcat.
- Entender e trabalhar com uma atividade e uma intent e saber interagir com elas.
O que você vai aprender
- Como o ciclo de vida da atividade funciona.
- Quando uma atividade é iniciada, pausada, interrompida e destruída.
- Sobre os métodos de callback do ciclo de vida associados às mudanças de atividade.
- O efeito de ações (como mudanças de configuração) que podem resultar em eventos de ciclo de vida da atividade.
- Como manter o estado da atividade em eventos de ciclo de vida.
Atividades deste laboratório
- Adicionar código ao app TwoActivities do exercício prático anterior para implementar os vários callbacks do ciclo de vida da atividade e incluir log statements.
- Observar as mudanças de estado à medida que o app é executado e você interage com cada atividade.
- Modificar o app para reter o estado da instância de uma atividade que é recriada inesperadamente em resposta ao comportamento do usuário ou a mudanças de configuração no dispositivo.
2. Visão geral do app
Neste exercício, você vai adicionar dados ao app TwoActivities (link em inglês). O app vai ter a mesma aparência e o mesmo comportamento do último codelab. Ele contém duas implementações de atividade e oferece ao usuário a capacidade de alternar entre elas. As mudanças que você fizer no app neste exercício não afetarão o comportamento visível ao usuário.
3. 3. Tarefa 1: adicionar callbacks do ciclo de vida ao app TwoActivities
Nesta tarefa, você vai implementar todos os métodos de callback do ciclo de vida da atividade para mostrar mensagens no logcat quando esses métodos forem invocados. Essas mensagens de registro permitem que você saiba quando o ciclo de vida da atividade muda de estado e como essas mudanças afetam o app durante a execução.
1.1 (Opcional) Copiar o projeto TwoActivities
Para as tarefas deste curso, você vai modificar o projeto TwoActivities existente criado no último exercício. Se você preferir manter o projeto TwoActivities anterior intacto, siga as etapas em Apêndice: utilitários para fazer uma cópia dele.
1.2 Implementar callbacks na MainActivity
- Abra o projeto TwoActivities no Android Studio e abra MainActivity em Project > painel Android.
- No método onCreate(), adicione os seguintes log statements:
Log.d(LOG_TAG, "-------");
Log.d(LOG_TAG, "onCreate");
- Adicione uma substituição para o callback onStart(), com uma instrução no registro desse evento:
@Override
public void onStart(){
super.onStart();
Log.d(LOG_TAG, "onStart");
}
Para um atalho, selecione Code > Override Methods no Android Studio. Uma caixa de diálogo aparecerá com todos os métodos possíveis que você pode substituir na classe. A escolha de um ou mais métodos de callback da lista insere um modelo completo para esses métodos, incluindo a chamada necessária para a superclasse.
- Use o método onStart() como modelo para implementar os callbacks de ciclo de vida onPause(), onRestart(), onResume(), onStop() e onDestroy().
Todos os métodos de callback têm as mesmas assinaturas (exceto o nome). Se você copiar e colar onStart() para criar esses outros métodos de callback, não se esqueça de atualizar o conteúdo para chamar o método correto na superclasse e registrar o método correto.
- Execute o app.
- Clique na guia "Logcat" na parte de baixo do Android Studio para mostrar o painel do Logcat. Você verá três mensagens de registro mostrando os três estados do ciclo de vida pelos quais a atividade passou desde o início:
D/MainActivity: -------
D/MainActivity: onCreate
D/MainActivity: onStart
D/MainActivity: onResume
1.3 Implementar callbacks do ciclo de vida na SecondActivity
Agora que você implementou os métodos de callback do ciclo de vida para a MainActivity, faça o mesmo para a SecondActivity.
- Abra a SecondActivity.
- Na parte superior da classe, adicione uma constante para a variável LOG_TAG:
private static final String LOG_TAG = SecondActivity.class.getSimpleName();
- Adicione os callbacks do ciclo de vida e os log statements à segunda atividade. É possível copiar e colar os métodos de callback da MainActivity.
- Adicione um log statement ao método returnReply() antes do método launch():
Log.d(LOG_TAG, "End SecondActivity");
1.4 Observar o registro durante a execução do app**
- Execute o app.
- Clique na guia "Logcat" na parte de baixo do Android Studio para mostrar o painel do Logcat.
- Digite "Atividade" na caixa de pesquisa. O logcat do Android pode ser muito longo e confuso. Como a variável LOG_TAG em cada classe contém as palavras MainActivity ou SecondActivity, essa palavra-chave permite filtrar o registro somente para as coisas em que você tem interesse.
Tente usar seu app e observe os eventos do ciclo de vida que ocorrem em resposta a diferentes ações. Em especial, tente realizar estas ações:
- Use o app normalmente (envie uma mensagem, responda com outra).
- Use o botão "Voltar" para voltar da segunda atividade para a atividade principal.
- Use a seta para cima na barra de apps para voltar da segunda atividade para a atividade principal.
- Gire o dispositivo tanto na atividade principal quanto na segunda atividade em diferentes momentos no seu app e observe o que acontece * no registro e na tela.
- Pressione o botão de visão geral (o botão quadrado à direita da página inicial) e feche o app (toque no X).
- Volte para a tela inicial e reinicie o app.
DICA: se você estiver executando o app em um emulador, poderá simular a rotação usando Control + F11 ou Control + Function + F11.
Código da solução da tarefa 1
Os snippets de código abaixo mostram o código da solução para a primeira tarefa.
MainActivity
Os snippets de código abaixo mostram o código adicionado na MainActivity, mas não a classe inteira.
O método onCreate():
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Log the start of the onCreate() method.
Log.d(LOG_TAG, "-------");
Log.d(LOG_TAG, "onCreate");
// Initialize all the view variables.
mMessageEditText = findViewById(R.id.editText_main);
mReplyHeadTextView = findViewById(R.id.text_header_reply);
mReplyTextView = findViewById(R.id.text_message_reply);
}
Os outros métodos de ciclo de vida:
@Override
protected void onStart() {
super.onStart();
Log.d(LOG_TAG, "onStart");
}
@Override
protected void onPause() {
super.onPause();
Log.d(LOG_TAG, "onPause");
}
@Override
protected void onRestart() {
super.onRestart();
Log.d(LOG_TAG, "onRestart");
}
@Override
protected void onResume() {
super.onResume();
Log.d(LOG_TAG, "onResume");
}
@Override
protected void onStop() {
super.onStop();
Log.d(LOG_TAG, "onStop");
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.d(LOG_TAG, "onDestroy");
}
SecondActivity
Os snippets de código abaixo mostram o código adicionado na SecondActivity, mas não a classe inteira.
Na parte de cima da classe SecondActivity:
private static final String LOG_TAG = SecondActivity.class.getSimpleName();
O método returnReply():
public void returnReply(View view) {
String reply = mReply.getText().toString();
Intent replyIntent = new Intent();
replyIntent.putExtra(EXTRA_REPLY, reply);
setResult(RESULT_OK, replyIntent);
Log.d(LOG_TAG, "End SecondActivity");
finish();
}
Os outros métodos de ciclo de vida:
Igual à MainActivity acima.
4. 4. Tarefa 2: salvar e restaurar o estado da instância da atividade
Dependendo dos recursos do sistema e do comportamento do usuário, cada atividade no seu app pode ser destruída e reconstruída com muito mais frequência do que você imagina.
Você pode ter notado esse comportamento na última seção ao girar o dispositivo ou emulador. A rotação do dispositivo é um exemplo de mudança na configuração do dispositivo. Embora a rotação seja a mais comum, todas as mudanças de configuração fazem com que a atividade atual seja destruída e recriada como se fosse nova. Se você não considerar esse comportamento no seu código, quando ocorrer uma mudança na configuração, o layout da atividade poderá ser revertido para a aparência padrão e os valores iniciais, e os usuários poderão perder o lugar, os dados ou o estado do progresso deles no app.
O estado de cada atividade é armazenado como um conjunto de pares de chave-valor em um objeto Bundle chamado de estado da instância da atividade. O sistema salva as informações de estado padrão no pacote do estado da instância pouco antes da atividade ser interrompida e transmite o pacote à nova instância da atividade a ser restaurada.
Para evitar a perda de dados em uma atividade quando ela é destruída e recriada inesperadamente, você precisa implementar o método onSaveInstanceState(). O sistema chama esse método na atividade (entre onPause() e onStop()) quando há a possibilidade de a atividade ser destruída e recriada.
Os dados salvos no estado da instância são específicos apenas para essa instância dessa atividade específica durante a sessão atual do app. Quando você interrompe e reinicia uma nova sessão do app, o estado da instância da atividade é perdido, e a atividade é revertida para a aparência padrão. Se você precisar salvar dados do usuário entre sessões do app, use as preferências compartilhadas ou um banco de dados. Você vai aprender sobre isso no futuro em um exercício prático.
2.1 Salvar o estado da instância de atividade com onSaveInstanceState()
Você deve ter notado que girar o dispositivo não afeta o estado da segunda atividade. Isso ocorre porque o layout e o estado da segunda atividade são gerados a partir do layout e da intent que a ativou. Mesmo que a atividade seja recriada, a intent ainda estará presente e os dados nessa intent ainda serão usados sempre que o método onCreate() for chamado na segunda atividade.
Além disso, você pode notar que, em cada Activity, qualquer texto digitado em elementos EditText de mensagem ou resposta é mantido, mesmo quando o dispositivo é girado. Isso ocorre porque as informações de estado de alguns dos elementos View no seu layout são salvas automaticamente nas mudanças de configuração, e o valor atual de um EditText é um desses casos.
O único estado da atividade em que você tem interesse são os elementos TextView no cabeçalho e no texto da resposta na atividade principal. Os dois elementos da TextView são invisíveis por padrão. Eles só aparecem quando você envia uma mensagem de volta para a atividade principal a partir da segunda atividade.
Nesta tarefa, você vai adicionar um código para preservar o estado da instância desses dois elementos da TextView usando onSaveInstanceState().
- Abra a MainActivity.
- Adicione esta implementação de esqueleto de onSaveInstanceState() à Activity ou use Código > Substituir métodos para inserir uma substituição de esqueleto.
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
}
- Verifique se o cabeçalho está visível no momento. Em caso afirmativo, coloque esse estado de visibilidade no pacote de estado com o método putBoolean() e a chave "reply_visible".
if (mReplyHeadTextView.getVisibility() == View.VISIBLE) {
outState.putBoolean("reply_visible", true);
}
Lembre-se de que o cabeçalho e o texto da resposta são marcados como invisíveis até que haja uma resposta da segunda atividade. Se o cabeçalho estiver visível, há dados de resposta que precisam ser salvos. Só estamos interessados nesse estado de visibilidade. O texto real do cabeçalho não precisa ser salvo porque ele nunca muda.
- Dentro dessa mesma verificação, adicione o texto de resposta ao pacote.
outState.putString("reply_text",mReplyTextView.getText().toString());
Se o cabeçalho estiver visível, você poderá presumir que a própria mensagem de resposta também está visível. Não é necessário testar nem salvar o estado de visibilidade atual da mensagem de resposta. Apenas o texto real da mensagem entra no estado Pacote com a chave "reply_text".
Você salva o estado apenas dos elementos de visualização que podem mudar depois que a atividade é criada. Os outros elementos View no seu app (o EditText, o Button) podem ser recriados a partir do layout padrão a qualquer momento.
O sistema salvará o estado de alguns elementos View, como o conteúdo do EditText.
2.2 Restaurar o estado da instância da atividade em onCreate()
Depois de salvar o estado da instância da atividade, também é necessário restaurá-lo quando a atividade é recriada. Você pode fazer isso em onCreate() ou implementando o callback onRestoreInstanceState(), que é chamado após onStart() depois que a atividade é criada.
Na maioria das vezes, o melhor lugar para restaurar o estado da atividade é em onCreate(), para garantir que a interface, incluindo o estado, esteja disponível o mais rápido possível. Às vezes, é conveniente fazer isso em onRestoreInstanceState() depois que toda a inicialização tiver sido feita ou permitir que subclasses decidam se a implementação padrão será usada.
- No método onCreate(), depois que as variáveis View forem inicializadas com findViewById(), adicione um teste para garantir que saveInstanceState não seja nulo.
// Initialize all the view variables.
mMessageEditText = findViewById(R.id.editText_main);
mReplyHeadTextView = findViewById(R.id.text_header_reply);
mReplyTextView = findViewById(R.id.text_message_reply);
// Restore the state.
if (savedInstanceState != null) {
}
Quando a atividade é criada, o sistema transmite o estado Bundle para onCreate() como único argumento. Na primeira vez que onCreate() for chamado e seu app for iniciado, o pacote será nulo. Não haverá estado na primeira vez que o app for iniciado. As chamadas subsequentes para onCreate() têm um pacote preenchido com os dados armazenados em onSaveInstanceState().
- Dentro dessa verificação, extraia a visibilidade atual (verdadeiro ou falso) do pacote com a chave "reply_visible".
if (savedInstanceState != null) {
boolean isVisible =
savedInstanceState.getBoolean("reply_visible");
}
- Adicione um teste abaixo da linha anterior para a variável isVisible.
if (isVisible) {
}
Se houver uma chave "response_visible" no pacote do estado (e "isVisible, portanto", "true"), você vai precisar restaurar o estado.
- No teste isVisible, torne o cabeçalho visível.
mReplyHeadTextView.setVisibility(View.VISIBLE);
- Receba a mensagem de resposta de texto do pacote com a chave "reply_text" e defina a TextView de resposta para mostrar essa string.
mReplyTextView.setText(savedInstanceState.getString("reply_text"));
- Torne a TextView de resposta visível também:
mReplyTextView.setVisibility(View.VISIBLE);
- Execute o app. Tente girar o dispositivo ou o emulador para garantir que a mensagem de resposta (se houver) permaneça na tela depois que a atividade for recriada.
Código da solução da tarefa 2
Os snippets de código abaixo mostram o código da solução para essa tarefa.
MainActivity
Os snippets de código abaixo mostram o código adicionado na MainActivity, mas não a classe inteira.
O método onSaveInstanceState():
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// If the heading is visible, message needs to be saved.
// Otherwise we're still using default layout.
if (mReplyHeadTextView.getVisibility() == View.VISIBLE) {
outState.putBoolean("reply_visible", true);
outState.putString("reply_text",
mReplyTextView.getText().toString());
}
}
O método onCreate():
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d(LOG_TAG, "-------");
Log.d(LOG_TAG, "onCreate");
// Initialize all the view variables.
mMessageEditText = findViewById(R.id.editText_main);
mReplyHeadTextView = findViewById(R.id.text_header_reply);
mReplyTextView = findViewById(R.id.text_message_reply);
// Restore the saved state.
// See onSaveInstanceState() for what gets saved.
if (savedInstanceState != null) {
boolean isVisible =
savedInstanceState.getBoolean("reply_visible");
// Show both the header and the message views. If isVisible is
// false or missing from the bundle, use the default layout.
if (isVisible) {
mReplyHeadTextView.setVisibility(View.VISIBLE);
mReplyTextView.setText(savedInstanceState
.getString("reply_text"));
mReplyTextView.setVisibility(View.VISIBLE);
}
}
}
O projeto completo:
Projeto do Android Studio: TwoActivitiesLifecycle (link em inglês)
5. Programação
Desafio: crie um app simples de lista de compras com uma atividade principal para a lista que o usuário está criando e uma segunda atividade para uma lista de itens comuns.
- A atividade principal precisa conter a lista a ser criada, que precisa ser composta por dez elementos TextView vazios.
- Um botão "Add Item" na atividade principal inicia uma segunda atividade que contém uma lista de itens comuns (queijo, arroz, maçãs e assim por diante). Use elementos Button para exibir os itens.
- A escolha de um item retorna o usuário à atividade principal e atualiza uma TextView vazia para incluir o item escolhido.
Use uma intent para transmitir informações de uma atividade para outra. O estado atual da lista de compras precisa ser salvo quando o usuário girar o dispositivo.
6. Resumo
- O ciclo de vida da atividade é um conjunto de estados pelos quais uma atividade migra, começando quando ela é criada pela primeira vez e terminando quando o sistema Android recupera os recursos dessa atividade.
- À medida que o usuário navega de uma atividade para outra, e dentro e fora do app, cada atividade se move entre estados no ciclo de vida da atividade.
- Cada estado do ciclo de vida da atividade tem um método de callback correspondente que você pode substituir na classe da atividade.
- Os métodos do ciclo de vida são onCreate(), onStart(), onPause(), onRestart(), onResume(), onStop(), onDestroy().
- A substituição de um método de callback do ciclo de vida permite adicionar um comportamento que ocorre quando a atividade faz a transição para esse estado.
- É possível adicionar modelos de métodos de substituição às classes no Android Studio em Code > Override.
- Mudanças na configuração do dispositivo, como a rotação, fazem com que a atividade seja destruída e recriada como se fosse nova.
- Uma parte do estado da atividade é preservada em uma mudança de configuração, incluindo os valores atuais dos elementos EditText. Para todos os outros dados, você precisa salvar esses dados explicitamente.
- Salvar o estado da instância da atividade no método onSaveInstanceState().
- Os dados de estado da instância são armazenados como pares simples de chave-valor em um pacote. Use os métodos Bundle para armazenar e extrair dados do pacote.
- Restaure o estado da instância em onCreate(), que é a maneira recomendada, ou onRestoreInstanceState(). Voltar