1. Olá!
Introdução
Nesta seção, você vai saber 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á conhece 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.
O que você já precisa saber
Você precisa saber:
- Criar e executar um projeto de app no Android Studio.
- Adicionar log statements ao app e observar esses logs no painel Logcat.
- Entender e trabalhar com uma
Activity
e umaIntent
e saber interagir com elas.
O que você vai aprender
- Como o ciclo de vida da
Activity
funciona. - Quando uma
Activity
é iniciada, pausada, interrompida e destruída. - Sobre os métodos de callback do ciclo de vida associados às mudanças da
Activity
. - O efeito de ações (como mudanças de configuração) que podem resultar em eventos de ciclo de vida da
Activity
. - Como preservar o estado da
Activity
em eventos de ciclo de vida.
O que você vai fazer
- Adicionar código ao app TwoActivities do exercício prático anterior para implementar os vários callbacks de ciclo de vida de
Activity
e incluir log statements. - Observar as mudanças de estado à medida que o app é executado e você interage com cada
Activity
no app. - Modificar o app para preservar o estado da instância de uma
Activity
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 código 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 Activity
e permite que o usuário navegue 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. 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 Activity
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 Activity
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 na última parte prática. Se você preferir manter o projeto TwoActivities anterior intacto, siga as etapas em Apêndice: utilitários para fazer uma cópia do projeto (links em inglês).
1.2 Implementar callbacks na MainActivity
- Abra o projeto TwoActivities no Android Studio e abra MainActivity no painel Project > Android.
- No método
onCreate()
, adicione estes 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 vidaonPause()
,onRestart()
,onResume()
,onStop()
eonDestroy()
.
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 certo 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 Logcat. Vão aparecer três mensagens de registro mostrando os três estados do ciclo de vida pelos quais a
Activity
passou:
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 MainActivity
, faça o mesmo para SecondActivity
.
- Abra SecondActivity.
- Na parte de cima 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
Activity
. Você pode Copiar e Colar os métodos de callback daMainActivity
. - Adicione um log statement ao método
returnReply()
antes do métodofinish()
:
Log.d(LOG_TAG, "End SecondActivity");
1.4 Observar os registros enquanto o app é executado
- Execute o app.
- Clique na guia Logcat na parte de baixo do Android Studio para mostrar o painel Logcat.
- Digite Activity 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 palavrasMainActivity
ouSecondActivity
, essa palavra-chave permite filtrar o registro apenas para as coisas em que você se interessa.
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
Activity
para aActivity
principal. - Use a seta para cima na barra de apps para voltar da segunda
Activity
para aActivity
principal. - Gire o dispositivo tanto na primeira quanto na segunda
Activity
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 do botão home) 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 com 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 em 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 em 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. Tarefa 2: salvar e restaurar o estado da instância da atividade
Dependendo dos recursos do sistema e do comportamento do usuário, cada Activity
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 de configuração do dispositivo. Embora a rotação seja a mais comum, todas as mudanças de configuração fazem com que a Activity
atual seja destruída e recriada como se fosse nova. Se você não considerar esse comportamento no seu código, quando uma mudança de configuração ocorrer, o layout da Activity
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 seu app.
O estado de cada Activity
é armazenado como um conjunto de pares de chave-valor em um objeto Bundle
chamado de estado da instância da Activity
. O sistema salva as informações de estado padrão no estado da instânciaBundle
antes da Activity
ser interrompida e transmite o Bundle
à nova instância da Activity
que será restaurada.
Para evitar a perda de dados de Activity
quando ela é destruída e recriada inesperadamente, você precisa implementar o método onSaveInstanceState()
. O sistema chama esse método na Activity
(entre onPause()
e onStop()
) quando há a possibilidade de a Activity
ser destruída e recriada.
Os dados salvos no estado da instância são específicos apenas para essa instância dessa Activity
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 Activity
é perdido e a Activity
é 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ê pode ter notado que girar o dispositivo não afeta o estado da segunda Activity
. Isso ocorre porque o layout e o estado da segunda Activity
são gerados a partir do layout e da Intent
que o ativou. Mesmo que a Activity
seja recriada, a Intent
ainda estará presente e os dados nessa Intent
ainda serão usados sempre que o método onCreate()
na segunda Activity
for chamado.
Além disso, você pode notar que, em cada Activity
, qualquer texto digitado em elementos EditText
de mensagem ou de resposta é mantido, mesmo quando o dispositivo é girado. Isso ocorre porque as informações de estado de alguns elementos View
no 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 Activity
em que você tem interesse são os elementos TextView
do cabeçalho e do texto da resposta na Activity
principal. Os dois elementos TextView
são invisíveis por padrão. Eles só aparecem quando você envia uma mensagem de volta para a Activity
principal a partir da segunda Activity
.
Nesta tarefa, você vai adicionar um código para preservar o estado da instância desses dois elementos TextView
usando onSaveInstanceState()
.
- Abra a MainActivity.
- Adicione essa implementação básica de
onSaveInstanceState()
àActivity
ou use Code > Override Methods para inserir uma substituição.
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
}
- Verifique se o cabeçalho está visível no momento. Caso esteja, coloque esse estado de visibilidade no estado
Bundle
com o métodoputBoolean()
e a chave"reply_visible"
.
if (mReplyHeadTextView.getVisibility() == View.VISIBLE) {
outState.putBoolean("reply_visible", true);
}
Não se esqueça que o cabeçalho e o texto da resposta são marcados como invisíveis até que haja uma resposta da segunda Activity
. 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
Bundle
.
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 Bundle
com a chave "reply_text"
.
Você salva o estado apenas dos elementos View
que podem mudar depois que a Activity
é criada. Os outros elementos View
no seu app (o EditText
e o Button
) podem ser recriados usando o layout padrão sempre que necessário.
O sistema salvará o estado de alguns elementos View
, como o conteúdo de EditText
.
2.2 Restaurar o estado da instância da atividade em onCreate()
Depois de salvar o estado da instância da Activity
, também é necessário restaurá-lo quando a Activity
é recriada. Você pode fazer isso em onCreate()
ou implementando o callback onRestoreInstanceState()
, que é chamado após onStart()
depois da criação da Activity
.
Na maioria das vezes, o melhor lugar para restaurar o estado da Activity
é 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 é concluída ou permitir que subclasses decidam se a implementação padrão será usada.
- No método
onCreate()
, depois que as variáveisView
forem inicializadas comfindViewById()
, adicione um teste para garantir quesavedInstanceState
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 Activity
é criada, o sistema transmite o estado Bundle
para onCreate()
como o único argumento. Na primeira vez que o método onCreate()
for chamado e o app for iniciado, o Bundle
será null
. Não há estado na primeira vez que o app for iniciado. As chamadas subsequentes para onCreate()
terão um pacote preenchido com os dados armazenados em onSaveInstanceState()
.
- Dentro dessa verificação, extraia a visibilidade atual (verdadeiro ou falso) do
Bundle
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 reply_visible
no estado Bundle
(e isVisible
for true
), você precisará restaurar o estado.
- No teste de
isVisible
, torne o cabeçalho visível.
mReplyHeadTextView.setVisibility(View.VISIBLE);
- Receba a mensagem de resposta de texto de
Bundle
com a chave"reply_text"
e defina a respostaTextView
para mostrar essa string.
mReplyTextView.setText(savedInstanceState.getString("reply_text"));
- Torne a resposta
TextView
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
Activity
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 em 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. Desafio de 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 mostrar os itens. - A escolha de um item retorna o usuário à atividade principal e atualiza um elemento
TextView
vazio para incluir o item escolhido.
Use uma Intent
para transmitir informações de uma Activity
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
Activity
passa, começando quando ela é criada pela primeira vez e terminando quando o sistema Android recupera os recursos dessaActivity
. - À medida que o usuário navega de uma
Activity
para outra, e dentro e fora do app, cadaActivity
se move entre estados no ciclo de vida daActivity
. - Cada estado do ciclo de vida da
Activity
tem um método de callback correspondente que você pode substituir na classeActivity
. - Os métodos do ciclo de vida são
onCreate()
,onStart()
,onPause()
,onRestart()
,onResume()
,onStop()
eonDestroy()
. - A substituição de um método de callback do ciclo de vida permite adicionar um comportamento que ocorre quando a
Activity
faz a transição para esse estado. - É possível adicionar modelos de métodos de substituição às classes no Android Studio com Code > Override.
- Mudanças na configuração do dispositivo, como a rotação, fazem com que a
Activity
seja destruída e recriada como se fosse nova. - Uma parte do estado da
Activity
é preservada em uma mudança de configuração, incluindo os valores atuais dos elementosEditText
. Para todos os outros dados, você precisa salvar esses dados explicitamente. - Salve o estado da instância da
Activity
no métodoonSaveInstanceState()
. - Os dados de estado da instância são armazenados como pares simples de chave-valor em um
Bundle
. Use os métodosBundle
para armazenar e extrair dados doBundle
. - Restaure o estado da instância em
onCreate()
, que é a maneira recomendada, ouonRestoreInstanceState()
.
7. Conceito relacionado
A documentação do conceito relacionado está em 2.2: Ciclo de vida e estado da atividade (link em inglês).
8. Saiba mais
Documentação do Android Studio:
Documentação do desenvolvedor Android:
9. Dever de casa
Esta seção lista as possíveis atividades de dever de casa para os alunos que estão fazendo este codelab como parte de um curso ministrado por um professor. Cabe ao professor fazer o seguinte:
- Atribuir o dever de casa, se necessário.
- Informar aos alunos como enviar deveres de casa.
- Atribuir nota aos deveres de casa.
Os professores podem usar essas sugestões o quanto quiserem, podendo passar os exercícios que acharem mais apropriados como dever de casa.
Se você estiver seguindo este codelab por conta própria, sinta-se à vontade para usar esses deveres de casa para testar seu conhecimento.
Criar e executar um app
- Crie um app com um layout que contenha um contador de
TextView
, umButton
para incrementar o contador e umEditText
. Como exemplo, confira a captura de tela abaixo. Não é necessário copiar exatamente o layout. - Adicione um gerenciador de cliques para o
Button
que incrementa o contador. - Execute o app e incremente o contador. Digite um texto no
EditText
. - Gire o dispositivo. Observe que o contador é redefinido, mas o
EditText
não. - Implemente
onSaveInstanceState()
para salvar o estado atual do app. - Atualize o método
onCreate()
para restaurar o estado do app. - Ao girar o dispositivo, o estado do app precisa ser preservado.
Responda estas perguntas
Pergunta 1
Se você executar o app de dever de casa antes de implementar onSaveInstanceState()
, o que acontecerá ao girar o dispositivo? Escolha uma:
- O
EditText
não conterá mais o texto que você digitou, mas o contador será preservado. - O contador será redefinido como 0, e o
EditText
não conterá mais o texto que você digitou. - O contador será redefinido como 0, mas o conteúdo do
EditText
será preservado. - O contador e o conteúdo do
EditText
serão preservados.
Pergunta 2
Quais métodos do ciclo de vida de Activity
são chamados quando ocorre uma mudança na configuração do dispositivo (como a rotação)? Escolha uma:
- O Android encerra imediatamente a
Activity
chamandoonStop()
. Seu código precisa reiniciar aActivity
. - O Android encerra a
Activity
chamandoonPause()
,onStop()
eonDestroy()
. Seu código precisa reiniciar aActivity
. - O Android encerra a
Activity
chamandoonPause()
,onStop()
eonDestroy()
e a reinicia novamente, chamandoonCreate()
,onStart()
eonResume()
. - O Android chama
onResume()
imediatamente.
Pergunta 3
Quando o método onSaveInstanceState()
é chamado no ciclo de vida da Activity
? Escolha uma:
onSaveInstanceState()
é chamado antes do métodoonStop()
.onSaveInstanceState()
é chamado antes do métodoonResume()
.onSaveInstanceState()
é chamado antes do métodoonCreate()
.onSaveInstanceState()
é chamado antes do métodoonDestroy()
.
Pergunta 4
Quais métodos de ciclo de vida da Activity
são melhores para salvar dados antes que a Activity
seja concluída ou destruída? Escolha uma:
onPause()
ouonStop()
onResume()
ouonCreate()
onDestroy()
onStart()
ouonRestart()
Enviar o app para avaliação
Orientação para os avaliadores
Verifique se o app tem estes recursos:
- Ele mostra um contador, um
Button
para incrementar esse contador e umEditText
. - Clicar em
Button
aumenta o contador em 1. - Quando o dispositivo é girado, os estados do contador e
EditText
são preservados. - A implementação de
MainActivity.java
usa o métodoonSaveInstanceState()
para armazenar o valor do contador. - A implementação de testes de
onCreate()
para a existência doBundle
dooutState
. Se esseBundle
existir, o valor do contador será restaurado e salvo no elementoTextView
.
10. Próximo codelab
Para encontrar o próximo codelab prático do curso de noções básicas para desenvolvedores Android (V2), consulte os codelabs para o curso Conceitos básicos para desenvolvedores Android (V2).
Para ter uma visão geral do curso, incluindo links para os capítulos do conceito, apps e slides, consulte Conceitos básicos para desenvolvedores Android (versão 2).