Estágios do ciclo de vida da atividade

1. Antes de começar

Neste codelab, você vai aprender sobre uma parte fundamental do Android: o ciclo de vida da atividade.

Durante o ciclo de vida, uma atividade transita entre diferentes estados e, às vezes, retorna a um deles. Essa transição de estados é conhecida como "ciclo de vida da atividade".

No Android, uma atividade é o ponto de entrada para a interação com o usuário.

Antes, uma atividade mostrava uma tela em um app. Com as práticas recomendadas atuais, uma atividade pode mostrar várias telas e alternar entre elas, conforme necessário.

O ciclo de vida da atividade se estende desde a criação até a destruição da atividade, quando o sistema recupera os recursos dela. À medida que o usuário navega para dentro e para fora de uma atividade, cada atividade transita entre diferentes estados no ciclo de vida.

Como desenvolvedor Android, você precisa compreender o ciclo de vida da atividade. Se as atividades não responderem corretamente às mudanças de estado do ciclo de vida, é possível que o app gere bugs estranhos, se comporte de maneira confusa para os usuários ou use muitos recursos do sistema Android. Entender o ciclo de vida do Android e responder corretamente às mudanças de estado do ciclo de vida é uma parte importante do desenvolvimento para Android.

Pré-requisitos

  • Saber o que é uma atividade e como a criar no app.
  • Conhecimento sobre o que o método onCreate() da atividade faz e os tipos de operações realizadas nesse método.

O que você vai aprender

  • Como mostrar informações de registro no Logcat.
  • Os conceitos básicos do ciclo de vida da Activity e os callbacks invocados quando a atividade se move entre estados.
  • Como modificar métodos de callback do ciclo de vida para realizar operações em diferentes momentos do ciclo de vida da atividade.

O que você vai criar

  • Como modificar um app inicial chamado "Dessert Clicker" para adicionar informações de registro mostradas no Logcat.
  • Como substituir os métodos de callback do ciclo de vida e registrar as mudanças no estado da atividade.
  • Como executar o app e anotar as informações de registro que aparecem quando a atividade é iniciada, interrompida e retomada.
  • Como implementar rememberSaveable para reter os dados do app que podem ser perdidos caso a configuração do dispositivo mude.

2. Visão geral do app

Neste codelab, você vai trabalhar com um app inicial chamado "Dessert Clicker". Nele, sempre que o usuário tocar em uma sobremesa na tela, o app a "compra" para o usuário. O app atualiza valores no layout para:

  • O número de sobremesas que foram "compradas".
  • A receita total das sobremesas "compradas".

245d0bdfc09f4d54.png

Este app contém vários bugs relacionados ao ciclo de vida do Android. Por exemplo, em algumas circunstâncias, o app redefine os valores das sobremesas para 0. Compreender o ciclo de vida do Android vai ajudar você a entender por que esses problemas ocorrem e como eles podem ser corrigidos.

Baixar o código inicial

No Android Studio, abra a pasta basic-android-kotlin-compose-training-dessert-clicker.

3. Explorar os métodos do ciclo de vida e adicionar registros básicos

Toda atividade tem um ciclo de vida. Esse termo é uma alusão aos ciclos de vida de vegetais e animais, como o de uma borboleta. Os diferentes estados da borboleta mostram o crescimento dela desde o ovo, passando por lagarta, pupa e borboleta até a morte.

Ciclo de vida da borboleta: crescimento desde o ovo, passando por lagarta, pupa e borboleta até a morte.

Da mesma forma, o ciclo de vida da atividade consiste nos diferentes estados de uma atividade, desde a primeira inicialização até a destruição, momento em que o sistema operacional (SO) recupera a memória dela. Normalmente, o ponto de entrada de um programa é o método main(). As atividades do Android, no entanto, começam com o método onCreate(). Esse método seria o equivalente à fase de ovo no exemplo acima. Você já usou atividades várias vezes ao longo deste curso e talvez reconheça o método onCreate(). Quando o usuário inicia o app, navega entre atividades, entra e sai do app, a atividade muda de estado.

O diagrama a seguir mostra todos os estados do ciclo de vida da atividade. Como indicado pelos nomes, esses estados representam o status da atividade. Observe que, ao contrário de uma borboleta, uma atividade pode alternar entre os estados ao longo do ciclo de vida, em vez de seguir apenas em uma direção.

ca808edb1c95f07a.png

Muitas vezes, você quer mudar algum comportamento ou executar um código quando o estado do ciclo de vida da atividade muda. Portanto, a própria classe Activity e todas as subclasses de Activity, como ComponentActivity, implementam um conjunto de métodos de callback do ciclo de vida. O Android invoca esses callbacks quando a atividade se move de um estado para outro, e você pode substituir esses métodos nas suas atividades para realizar tarefas em resposta a essas mudanças de estado do ciclo de vida. O diagrama abaixo mostra os estados do ciclo de vida com os callbacks substituíveis que estão disponíveis.

Esquema do ciclo de vida da atividade

É importante saber quando o Android invoca os callbacks substituíveis e o que fazer em cada método deles, mas ambos os diagramas são complexos e podem ser confusos. Neste codelab, em vez de apenas ler o que cada estado e callback significam, você vai fazer um trabalho de detetive para entender o ciclo de vida da atividade do Android.

Etapa 1: examinar o método onCreate() e adicionar registros

Para descobrir o que está acontecendo com o ciclo de vida do Android, é útil saber quando os vários métodos de ciclo de vida são chamados. Essas informações ajudam você a identificar onde algo está errado no app Dessert Clicker.

Uma maneira simples de determinar essas informações é usar a funcionalidade de geração de registros do Android. A geração de registros permite que você escreva mensagens curtas em um console enquanto o app está em execução. Isso pode ser usado para ver quando diferentes callbacks são acionados.

  1. Execute o app DessertClicker e toque várias vezes na foto de uma sobremesa. Observe como o valor de Desserts sold e o valor total em dólares mudam.
  2. Abra MainActivity.kt e examine o método onCreate() dessa atividade:
override fun onCreate(savedInstanceState: Bundle?) {
    // ...
}

No diagrama do ciclo de vida da atividade, talvez você reconheça o método onCreate(), porque já usou esse callback antes. Ele é o único método que todas as atividades precisam implementar. É no método onCreate() que você precisa fazer todas as inicializações únicas para a atividade. Por exemplo, em onCreate(), você chama setContent(), que especifica o layout da interface da atividade.

O método onCreate do ciclo de vida

O método onCreate() do ciclo de vida é chamado uma vez, logo após a inicialização da atividade, quando o SO cria o novo objeto Activity na memória. Depois que onCreate() é executado, a atividade é considerada criada.

  1. Adicione a constante a seguir no nível superior do MainActivity.kt, acima da declaração de classe class MainActivity.

Uma boa prática geral é declarar uma constante TAG no arquivo, já que o valor dela não vai mudar.

Para a marcar como uma constante de tempo de compilação, use const ao declarar a variável. Uma constante de tempo de compilação é um valor conhecido durante a compilação.

private const val TAG = "MainActivity"
  1. No método onCreate(), logo após a chamada para super.onCreate(), adicione a seguinte linha:
Log.d(TAG, "onCreate Called")
  1. Importe a classe Log, se necessário (pressione Alt+Enter ou Option+Enter em um Mac e selecione Import). Se você ativou as importações automáticas, isso acontece automaticamente.
import android.util.Log

A classe Log escreve mensagens no Logcat. O Logcat é o console usado para registrar mensagens. As mensagens do Android sobre seu app são exibidas nesse local, incluindo as mensagens que você envia explicitamente para o registro com o método Log.d() ou outros métodos da classe Log.

Há três aspectos importantes da instrução Log:

  • A prioridade da mensagem de registro, ou seja, a importância da mensagem. Nesse caso, o Log.v() registra mensagens detalhadas. O método Log.d() escreve uma mensagem de depuração. Outros métodos na classe Log incluem: Log.i() para mensagens com informações, Log.w() para avisos e Log.e() para mensagens de erros.
  • O registro tag (o primeiro parâmetro). Neste caso, "MainActivity". A tag é uma string que facilita a localização de mensagens de registro no Logcat. Normalmente, ela é o nome da classe.
  • A mensagem de registro chamada msg (o segundo parâmetro) é uma string curta. Neste caso, "onCreate Called".

a4ff4aa74384ff6.png

  1. Compile e execute o app Dessert Clicker. Você não vai notar diferenças de comportamento no app quando tocar na sobremesa. No Android Studio, na parte de baixo da tela, clique na guia Logcat.

cedcce52592c6665.png

  1. Na janela Logcat, digite tag:MainActivity no campo de pesquisa.

37080c4e00561b0.png

O Logcat pode ter muitas mensagens, e a maioria delas não é útil para você. É possível filtrar as entradas do Logcat de várias maneiras, mas a pesquisa é a mais fácil. Como você utilizou a MainActivity como tag de registro no código, é possível usá-la para filtrar o registro. A mensagem de registro inclui a data e a hora, a tag de registro, o nome do pacote (com.example.dessertclicker) e a mensagem. Como essa mensagem aparece no registro, você sabe que o método onCreate() foi executado.

Etapa 2: implementar o método onStart()

O método do ciclo de vida onStart() é chamado logo depois de onCreate(). Depois que onStart() é executado, sua atividade fica visível na tela. Ao contrário de onCreate(), que é chamado apenas uma vez para inicializar a atividade, onStart() pode ser chamado pelo sistema várias vezes durante o ciclo de vida da atividade.

a357d2291de472d9.png

onStart() é pareado com um método do ciclo de vida onStop() correspondente. Se o usuário inicia o app e retorna à tela inicial do dispositivo, a atividade é interrompida e não fica mais visível na tela.

  1. No Android Studio, com MainActivity.kt aberto e o cursor na classe MainActivity, selecione Code > Override Methods… ou pressione Control+O. Uma caixa de diálogo vai aparecer com uma longa lista de todos os métodos que você pode substituir nessa classe.

20c34cbad8dce892.png

  1. Comece a digitar onStart para procurar o método correto. Para rolar para o próximo item correspondente, use a seta para baixo. Selecione onStart() na lista e clique em OK para inserir o código boilerplate de substituição. O código vai ficar assim:
override fun onStart() {
    super.onStart()
}
  1. No método onStart(), adicione uma mensagem de registro:
override fun onStart() {
    super.onStart()
    Log.d(TAG, "onStart Called")
}
  1. Compile e execute o app Dessert Clicker e abra o painel do Logcat.
  2. Digite tag:MainActivity no campo de pesquisa para filtrar o registro. Os métodos onCreate() e onStart() foram chamados um após o outro, e sua atividade está visível na tela.
  3. Pressione o botão Home no dispositivo e use a tela "Recentes" para retornar à atividade. A atividade continua de onde parou, com todos os mesmos valores, e onStart() é registrado uma segunda vez no Logcat. O método onCreate() não é chamado de novo.
2024-04-26 14:54:48.721  5386-5386  MainActivity            com.example.dessertclicker           D  onCreate Called
2024-04-26 14:54:48.756  5386-5386  MainActivity            com.example.dessertclicker           D  onStart Called
2024-04-26 14:55:41.674  5386-5386  MainActivity            com.example.dessertclicker           D  onStart Called

Etapa 3: adicionar mais log statements

Nesta etapa, você vai implementar a geração de registros para todos os outros métodos do ciclo de vida.

  1. Substitua os métodos restantes do ciclo de vida na MainActivity e adicione log statements para cada um, como mostrado neste código:
override fun onResume() {
    super.onResume()
    Log.d(TAG, "onResume Called")
}

override fun onRestart() {
    super.onRestart()
    Log.d(TAG, "onRestart Called")
}

override fun onPause() {
    super.onPause()
    Log.d(TAG, "onPause Called")
}

override fun onStop() {
    super.onStop()
    Log.d(TAG, "onStop Called")
}

override fun onDestroy() {
    super.onDestroy()
    Log.d(TAG, "onDestroy Called")
}
  1. Compile e execute o Dessert Clicker novamente e examine o Logcat.

Desta vez, além de onCreate() e onStart(), há uma mensagem de registro para o callback do ciclo de vida de onResume().

2024-04-26 14:56:48.684  5484-5484  MainActivity            com.example.dessertclicker           D  onCreate Called
2024-04-26 14:56:48.709  5484-5484  MainActivity            com.example.dessertclicker           D  onStart Called
2024-04-26 14:56:48.713  5484-5484  MainActivity            com.example.dessertclicker           D  onResume Called

Quando uma atividade é iniciada desde o começo, você vê os três callbacks do ciclo de vida chamados nesta ordem:

  • onCreate(), quando o sistema cria o app.
  • onStart() deixa o app visível na tela, mas o usuário ainda não pode interagir com ele.
  • onResume() coloca o app em primeiro plano, e o usuário agora pode interagir com ele.

Apesar do nome, o método onResume() é chamado na inicialização, mesmo se não houver nada para retomar.

Esquema do ciclo de vida da atividade

4. Explorar casos de uso do ciclo de vida

Agora que você configurou o Dessert Clicker para a geração de registros, já pode começar a usar o app e explorar como os callbacks do ciclo de vida são acionados.

Caso de uso 1: como abrir e fechar a atividade

Comece com o caso de uso mais básico, que é iniciar o app pela primeira vez e fechar.

  1. Compile e execute o app Dessert Clicker, se ele ainda não estiver em execução. Como você viu anteriormente, os callbacks onCreate(), onStart() e onResume() são chamados quando a atividade é inicializada pela primeira vez.
2024-04-26 14:56:48.684  5484-5484  MainActivity            com.example.dessertclicker           D  onCreate Called
2024-04-26 14:56:48.709  5484-5484  MainActivity            com.example.dessertclicker           D  onStart Called
2024-04-26 14:56:48.713  5484-5484  MainActivity            com.example.dessertclicker           D  onResume Called
  1. Toque no cupcake algumas vezes.
  2. Toque no botão Voltar do dispositivo.

No Logcat, onPause() e onStop() são chamados nessa ordem.

2024-04-26 14:58:19.984  5484-5484  MainActivity            com.example.dessertclicker           D  onPause Called
2024-04-26 14:58:20.491  5484-5484  MainActivity            com.example.dessertclicker           D  onStop Called
2024-04-26 14:58:20.517  5484-5484  MainActivity            com.example.dessertclicker           D  onDestroy Called

Nesse caso, o uso do botão Voltar faz com que a atividade e o app sejam removidos da tela e movidos para a parte de trás da pilha de atividades.

O SO Android pode fechar a atividade se o código chamar manualmente o método finish() ou se o usuário forçar o encerramento do app. Por exemplo, o usuário pode forçar o encerramento ou fechar o app na tela "Recentes". O SO também pode encerrar a atividade por conta própria se o app não estiver sendo mostrado na tela há muito tempo. O Android faz isso para preservar a duração da bateria e recuperar os recursos que o app estava usando para que eles fiquem disponíveis a outros apps. Esses são apenas alguns exemplos de por que o sistema Android destrói sua atividade. Há casos adicionais em que o sistema Android destrói sua atividade sem mostrar um aviso.

Caso de uso 2: como sair e voltar à atividade

Agora que você iniciou e fechou o app , já viu a maioria dos estados do ciclo de vida de quando a atividade é criada pela primeira vez. Você também viu a maioria dos estados do ciclo de vida em que a atividade passa quando é fechada. No entanto, à medida que os usuários interagem com dispositivos Android, eles alternam entre apps, voltam para a tela inicial, iniciam novos apps e processam interrupções de outras atividades, como chamadas.

Sua atividade não é fechada totalmente toda vez que o usuário sai dela.

  • Quando sua atividade não está mais visível na tela, esse status é conhecido como segundo plano. O oposto disso é quando a atividade está em primeiro plano ou na tela.
  • Quando o usuário retorna para o app, a mesma atividade é reiniciada e fica visível novamente. Essa parte do ciclo de vida é chamada de ciclo de vida visível do app.

Quando seu app está em segundo plano, ele geralmente não precisa estar em execução ativamente para preservar os recursos do sistema e a duração da bateria. Use o ciclo de vida e os callbacks da Activity para saber quando o app está sendo movido para o segundo plano e pausar as operações em andamento. Em seguida, reinicie as operações quando o app for para o primeiro plano.

Nesta etapa, você observa o ciclo de vida da atividade quando o app vai para o segundo plano e retorna ao primeiro plano novamente.

  1. Quando o app Dessert Clicker estiver em execução, clique no cupcake algumas vezes.
  2. Pressione o botão home do dispositivo e observe o Logcat no Android Studio. Retornar à tela inicial coloca o app em segundo plano em vez de o encerrar. Os métodos onPause() e onStop() são chamados.
2024-04-26 15:00:04.905  5590-5590  MainActivity            com.example.dessertclicker           D  onPause Called
2024-04-26 15:00:05.430  5590-5590  MainActivity            com.example.dessertclicker           D  onStop Called

Quando onPause() é chamado, o app não está mais em foco. Depois de onStop(), o app não fica mais visível na tela. Embora a atividade seja interrompida, o objeto Activity ainda está na memória em segundo plano. O SO Android não destruiu a atividade. O usuário ainda pode retornar ao app. Dessa forma, o Android guarda os recursos da atividade.

c470ee28ab7f8a1a.png

  1. Use a tela "Recentes" para retornar ao app. No emulador, essa tela pode ser acessada pelo botão quadrado do sistema, mostrado na imagem abaixo.

No Logcat, a atividade é reiniciada por onRestart() e onStart() e depois é retomada por onResume().

bc156252d977e5ae.png

2024-04-26 15:00:39.371  5590-5590  MainActivity            com.example.dessertclicker           D  onRestart Called
2024-04-26 15:00:39.372  5590-5590  MainActivity            com.example.dessertclicker           D  onStart Called
2024-04-26 15:00:39.374  5590-5590  MainActivity            com.example.dessertclicker           D  onResume Called

Quando a atividade retorna para o primeiro plano, o método onCreate() não é chamado novamente. O objeto da atividade não foi destruído, por isso não precisa ser criado novamente. O método onRestart() é chamado em vez de onCreate(). Desta vez, quando a atividade retornar ao primeiro plano, o número de Desserts sold é mantido.

  1. Inicie pelo menos um app que não seja o Dessert Clicker para que o dispositivo tenha alguns apps na tela "Recentes".
  2. Vá até a tela "Recentes" e abra outra atividade recente. Depois, volte para a tela de apps recentes e mova o Dessert Clicker para o primeiro plano.

Os mesmos callbacks vão aparecer no Logcat, como ao pressionar o botão Home. onPause() e onStop() são chamados quando o app entra em segundo plano. Depois, onRestart(), onStart() e onResume() são chamados quando ele volta ao primeiro plano.

Esses métodos são chamados quando o app é interrompido e passa para o segundo plano ou quando ele é reiniciado e volta para o primeiro plano. Se for necessário realizar alguma tarefa no app nesses casos, modifique o método de callback do ciclo de vida relevante.

Caso de uso 3: ocultar parcialmente a atividade

Você aprendeu que, quando um app é iniciado e onStart() é chamado, ele fica visível na tela. Quando onResume() é chamado, o app recebe o foco do usuário, ou seja, o usuário pode interagir com o app. A parte do ciclo de vida em que o app está totalmente na tela e com foco do usuário é conhecida como ciclo de vida em primeiro plano.

Quando o app é movido para segundo plano, o foco é perdido após onPause(), e o app não fica visível após onStop().

A diferença entre foco e visibilidade é importante. É possível que uma atividade fique parcialmente visível na tela, mas não tenha o foco do usuário. Nesta etapa, você vai ver um caso em que uma atividade está parcialmente visível, mas não tem o foco do usuário.

  1. Com o app Dessert Clicker em execução, clique no botão Share na parte superior direita da tela.

A atividade de compartilhamento vai aparecer na metade de baixo da tela, mas a atividade do app ainda ficará visível na metade de cima.

677c190d94e57447.pngca6285cbbe3801cf.png

  1. Analise o Logcat e observe que apenas o onPause() foi chamado.
2024-04-26 15:01:49.535  5590-5590  MainActivity            com.example.dessertclicker           D  onPause Called

Neste caso de uso, onStop() não é chamado, porque a atividade ainda está parcialmente visível. Mas ela não tem o foco do usuário, e não é possível interagir com ela. A atividade de "compartilhamento" que está em primeiro plano tem o foco do usuário.

Por que essa diferença é importante? O método onPause() geralmente interrompe a atividade apenas por um curto período antes de o usuário retornar para ela ou navegar para outra atividade ou app. Geralmente, é recomendável continuar atualizando a IU para que o restante do app não pareça travar.

O código executado no método onPause() bloqueia a exibição de outros elementos. Portanto, use um código leve em onPause(). Por exemplo, se o usuário receber uma chamada, o código no onPause() pode atrasar a notificação da chamada recebida.

  1. Clique fora da caixa de diálogo de compartilhamento para retornar ao app. Observe que o onResume() é chamado.

Tanto onResume() quanto onPause() estão relacionados ao foco. O método onResume() é chamado quando a atividade tem o foco, e onPause() é chamado quando a atividade perde o foco.

5. Explorar mudanças de configuração

Há outro caso no gerenciamento do ciclo de vida da atividade que é importante entender: como as mudanças de configuração afetam o ciclo de vida das suas atividades.

Uma mudança de configuração ocorre quando o estado do dispositivo muda de forma tão radical que a maneira mais fácil do sistema resolver a mudança é encerrar completamente e recriar a atividade. Por exemplo, se o usuário muda o idioma do dispositivo, todo o layout pode ser alterado para acomodar diferentes direções de texto e comprimentos de string. Se o usuário conecta o dispositivo a uma base ou adiciona um teclado físico, o layout do app pode usar um layout ou tamanho de exibição diferente. E se a orientação do dispositivo mudar (se ele é girado do modo retrato para paisagem ou vice-versa), é possível que o layout mude para se ajustar à nova orientação. Vejamos como o app se comporta nesse caso.

O último callback do ciclo de vida a ser demonstrado é onDestroy(), que é chamado depois de onStop(). Ele é chamado pouco antes de a atividade ser destruída. Isso pode acontecer quando o código do app chama finish() ou o sistema precisa destruir e recriar a atividade devido a uma mudança de configuração.

A mudança de configuração faz com que onDestroy() seja chamado.

A rotação da tela é um tipo de mudança de configuração que faz com que a atividade seja encerrada e reiniciada. Para simular essa mudança e examinar os efeitos dela, conclua as seguintes etapas:

  1. Compile e execute seu app.
  2. Verifique se o bloqueio de rotação de tela no emulador está desativado.
  3. Gire o dispositivo ou o emulador para o modo paisagem. Você pode girar o emulador para a esquerda ou direita usando os botões de rotação.
  4. Analise o Logcat e entenda que, quando a atividade é encerrada, ele chama onPause(), onStop() e onDestroy(), nessa ordem.
2024-04-26 15:03:32.183  5716-5716  MainActivity            com.example.dessertclicker           D  onPause Called
2024-04-26 15:03:32.185  5716-5716  MainActivity            com.example.dessertclicker           D  onStop Called
2024-04-26 15:03:32.205  5716-5716  MainActivity            com.example.dessertclicker           D  onDestroy Called

Perda de dados durante a rotação do dispositivo

  1. Compile e execute seu app, depois abra o Logcat.
  2. Clique no cupcake algumas vezes e observe que as sobremesas vendidas e a receita total não são zero.
  3. Verifique se o bloqueio de rotação de tela no emulador está desativado.
  4. Gire o dispositivo ou o emulador para o modo paisagem. Você pode girar o emulador para a esquerda ou direita usando os botões de rotação.

11c9d83a11651608.png

  1. Analise a saída no Logcat. Filtre a saída na MainActivity.
2024-04-26 15:04:29.356  5809-5809  MainActivity            com.example.dessertclicker           D  onCreate Called
2024-04-26 15:04:29.378  5809-5809  MainActivity            com.example.dessertclicker           D  onStart Called
2024-04-26 15:04:29.382  5809-5809  MainActivity            com.example.dessertclicker           D  onResume Called
2024-04-26 15:06:52.168  5809-5809  MainActivity            com.example.dessertclicker           D  onPause Called
2024-04-26 15:06:52.183  5809-5809  MainActivity            com.example.dessertclicker           D  onStop Called
2024-04-26 15:06:52.219  5809-5809  MainActivity            com.example.dessertclicker           D  onDestroy Called
2024-04-26 15:06:52.302  5809-5809  MainActivity            com.example.dessertclicker           D  onCreate Called
2024-04-26 15:06:52.308  5809-5809  MainActivity            com.example.dessertclicker           D  onStart Called
2024-04-26 15:06:52.312  5809-5809  MainActivity            com.example.dessertclicker           D  onResume Called

Quando o dispositivo ou emulador gira a tela, o sistema chama todos os callbacks do ciclo de vida para encerrar a atividade. Em seguida, enquanto a atividade é recriada, o sistema chama todos os callbacks do ciclo de vida para iniciá-la.

Quando o dispositivo é girado e a atividade é encerrada e recriada, ela é reiniciada com valores padrão. Ou seja, a imagem da sobremesa, o número de sobremesas vendidas e a receita são redefinidos como zero.

Para saber por que esses valores estão sendo redefinidos e como os corrigir, você precisa aprender sobre o ciclo de vida de uma função de composição e como ela sabe observar e manter o respectivo estado.

Ciclo de vida de um elemento combinável

A IU do app é criada inicialmente pela execução de funções de composição em um processo chamado "Composição".

Quando o estado do app muda, uma recomposição é agendada. A recomposição acontece quando o Compose executa novamente as funções combináveis que podem ter mudado de estado e cria uma interface atualizada. A composição é atualizada para refletir essas mudanças.

A única maneira de criar ou atualizar uma composição é pela composição inicial dela e pelas recomposições subsequentes.

As funções de composição têm um ciclo de vida próprio que é independente do ciclo de vida da atividade. O ciclo de vida delas é composto pelos seguintes eventos: entrar na composição, recompor 0 ou mais vezes e, depois, sair da composição.

Para que o Compose acompanhe e acione uma recomposição, ele precisa saber quando o estado mudou. Para indicar ao Compose que ele precisa rastrear o estado de um objeto, o objeto precisa ser do tipo State ou MutableState. O tipo State é imutável e só pode ser lido. Um tipo MutableState é mutável e permite leituras e gravações.

Você já viu e usou o MutableState no app Lemonade e no app Tip Time em codelabs anteriores.

Para criar a variável mutável revenue, declare-a usando mutableStateOf. 0 é o valor padrão inicial.

var revenue = mutableStateOf(0)

Embora isso seja suficiente para que o Compose acione uma recomposição quando o valor da receita mudar, não basta manter o valor atualizado. Sempre que o elemento combinável for executado novamente, ele vai reinicializar o valor da receita para o valor padrão inicial de 0.

Para instruir o Compose a reter e reutilizar o valor durante as recomposições, é necessário declarar o valor com a API remember.

var revenue by remember { mutableStateOf(0) }

Se o valor de revenue mudar, o Compose vai programar a recomposição de todas as funções de composição que leem esse valor.

Embora o Compose lembre o estado da receita durante as recomposições, ele não retém esse estado durante uma mudança de configuração. Para que o Compose retenha o estado durante uma mudança de configuração, use rememberSaveable.

Para mais informações e práticas recomendadas, consulte o codelab Introdução ao estado no Compose.

Usar rememberSaveable para salvar valores em todas as mudanças de configuração

Use a função rememberSaveable para salvar os valores necessários se o SO Android destruir e recriar a atividade.

Para salvar valores durante as recomposições, é necessário usar remember. Use rememberSaveable para salvar valores durante recomposições E mudanças de configuração.

Salvar o valor usando rememberSaveable garante que ele esteja disponível quando a atividade for restaurada, se necessário.

  1. No MainActivity, atualize o grupo de cinco variáveis que atualmente usam remember para rememberSaveable.
var revenue by remember { mutableStateOf(0) }
...
var currentDessertImageId by remember {
    mutableStateOf(desserts[currentDessertIndex].imageId)
}
var revenue by rememberSaveable { mutableStateOf(0) }
...
var currentDessertImageId by rememberSaveable {
    mutableStateOf(desserts[currentDessertIndex].imageId)
}
  1. Compile e execute seu app.
  2. Clique no cupcake algumas vezes e observe que as sobremesas vendidas e a receita total não são zero.
  3. Gire o dispositivo ou o emulador para o modo paisagem.
  4. Observe que, depois que a atividade é destruída e recriada, a imagem da sobremesa, as sobremesas vendidas e a receita total são restauradas para os valores anteriores.

6. Código da solução

7. Resumo

Ciclo de vida da atividade

  • O ciclo de vida da atividade é um conjunto de estados pelos quais uma atividade passa. Ele começa quando o SO Android cria a atividade pela primeira vez e termina quando ele a destrói.
  • Quando o usuário navega entre as atividades e dentro e fora do app, cada atividade se move entre os estados do ciclo de vida da atividade.
  • Cada estado do ciclo de vida da atividade tem um método de callback correspondente que você pode modificar na classe Activity. O conjunto principal de métodos do ciclo de vida é: onCreate(), onRestart(), onStart(), onResume(), onPause(), onStop(), onDestroy().
  • Para adicionar um comportamento que ocorre quando a atividade passa para um estado do ciclo de vida, substitua o método de callback do estado.
  • Para adicionar o esqueleto de métodos de substituição às classes no Android Studio, selecione Code > Override Methods… ou pressione Control+O.

Geração de registros com a classe Log

  • A API Android Logging e especificamente a classe Log permitem escrever mensagens curtas que são exibidas no Logcat no Android Studio.
  • Use Log.d() para escrever uma mensagem de depuração. Esse método recebe dois argumentos: a tag log, que geralmente é o nome da classe, e a mensagem log, uma string curta.
  • Use a janela Logcat do Android Studio para ver os registros do sistema, incluindo as mensagens que você escrever.

Mudanças de configuração

  • Uma mudança de configuração ocorre quando o estado do dispositivo muda de forma tão radical que a maneira mais fácil do sistema resolver a mudança é destruir e recriar a atividade.
  • O exemplo mais comum de uma mudança de configuração é quando o usuário gira o dispositivo do modo de retrato para o de paisagem ou vice-versa. Uma mudança de configuração também pode ocorrer quando o idioma do dispositivo muda ou quando o usuário conecta um teclado físico.
  • Quando uma mudança de configuração ocorre, o Android invoca todos os callbacks de desligamento do ciclo de vida da atividade. O Android reinicia a atividade do zero, executando todos os callbacks de inicialização do ciclo de vida.
  • Quando o Android encerra um app devido a uma mudança de configuração, ele reinicia a atividade usando onCreate().
  • Para salvar um valor que precisa sobreviver a uma mudança de configuração, declare as variáveis dele com rememberSaveable.

Saiba mais