Este codelab faz parte do curso Conceitos básicos do Kotlin para Android. Você vai aproveitar mais este curso se fizer os codelabs em sequência. Todos os codelabs do curso estão listados na página inicial dos codelabs de princípios básicos do Kotlin para Android.
Introdução
No último codelab, você aprendeu sobre os ciclos de vida Activity e Fragment e conheceu os métodos chamados quando o estado do ciclo de vida muda em atividades e fragmentos. Neste codelab, você vai conhecer o ciclo de vida da atividade com mais detalhes. Você também vai aprender sobre a biblioteca de ciclo de vida do Android Jetpack, que ajuda a gerenciar eventos de ciclo de vida com um código mais organizado e fácil de manter.
O que você já precisa saber
- O que é uma atividade e como criá-la no seu app.
- Os conceitos básicos dos ciclos de vida de
ActivityeFragmente os callbacks invocados quando uma atividade se move entre estados. - Como substituir os métodos de callback do ciclo de vida
onCreate()eonStop()para realizar operações em diferentes momentos do ciclo de vida da atividade ou do fragmento.
O que você vai aprender
- Como configurar, iniciar e interromper partes do app nos callbacks do ciclo de vida.
- Como usar a biblioteca de ciclo de vida do Android para criar um observador de ciclo de vida e facilitar o gerenciamento do ciclo de vida de atividades e fragmentos.
- Como os encerramentos de processos do Android afetam os dados no seu app e como salvar e restaurar esses dados automaticamente quando o Android fecha o app.
- Como a rotação do dispositivo e outras mudanças de configuração criam mudanças nos estados do ciclo de vida e afetam o estado do app.
O que você aprenderá
- Modifique o app DessertClicker para incluir uma função de timer e inicie e pare esse timer em vários momentos do ciclo de vida da atividade.
- Modifique o app para usar a biblioteca de ciclo de vida do Android e converta a classe
DessertTimerem um observador de ciclo de vida. - Configure e use o Android Debug Bridge (
adb) para simular o encerramento do processo do app e os callbacks do ciclo de vida que ocorrem depois. - Implemente o método
onSaveInstanceState()para reter os dados do app que podem ser perdidos se ele for fechado de forma inesperada. Como adicionar um código para restaurar esses dados quando o app for iniciado novamente.
Neste codelab, você vai expandir o app DessertClicker do codelab anterior. Você adiciona um timer em segundo plano e converte o app para usar a biblioteca de ciclo de vida do Android.

No codelab anterior, você aprendeu a observar os ciclos de vida de atividades e fragmentos substituindo vários callbacks de ciclo de vida e registrando quando o sistema invoca esses callbacks. Nesta tarefa, você vai conhecer um exemplo mais complexo de gerenciamento de tarefas do ciclo de vida no app DessertClicker. Você vai usar um timer que imprime uma instrução de registro a cada segundo, com a contagem do número de segundos em que ele está em execução.
Etapa 1: configurar o DessertTimer
- Abra o app DessertClicker do último codelab. (Você pode baixar o DessertClickerLogs aqui se não tiver o app.)
- Na visualização Project, expanda java > com.example.android.dessertclicker e abra
DessertTimer.kt. No momento, todo o código está comentado, então ele não é executado como parte do app. - Selecione todo o código na janela do editor. Selecione Code > Comment with Line Comment ou pressione
Control+/(Command+/em um Mac). Esse comando remove o comentário de todo o código no arquivo. O Android Studio pode mostrar erros de referência não resolvidos até que você recrie o app. - Observe que a classe
DessertTimerincluistartTimer()estopTimer(), que iniciam e interrompem o timer. QuandostartTimer()está em execução, o timer imprime uma mensagem de registro a cada segundo, com a contagem total dos segundos em que o tempo está sendo executado. O métodostopTimer(), por sua vez, interrompe o timer e as instruções de registro.
- Abra
MainActivity.kt. Na parte de cima da classe, logo abaixo da variáveldessertsSold, adicione uma variável para o timer:
private lateinit var dessertTimer : DessertTimer;- Role a tela para baixo até
onCreate()e crie um novo objetoDessertTimerlogo após a chamada parasetOnClickListener():
dessertTimer = DessertTimer()
Agora que você tem um objeto de timer de sobremesa, considere onde iniciar e parar o timer para que ele seja executado apenas quando a atividade estiver na tela. Você vai conferir algumas opções nas próximas etapas.
Etapa 2: iniciar e interromper o timer
O método onStart() é chamado pouco antes de a atividade ficar visível. O método onStop() é chamado depois que a atividade deixa de ficar visível. Esses callbacks parecem bons candidatos para iniciar e parar o timer.
- Na classe
MainActivity, inicie o timer no callbackonStart():
override fun onStart() {
super.onStart()
dessertTimer.startTimer()
Timber.i("onStart called")
}- Parar o timer em
onStop():
override fun onStop() {
super.onStop()
dessertTimer.stopTimer()
Timber.i("onStop Called")
}- Compile e execute o app. No Android Studio, clique no painel Logcat. Na caixa de pesquisa do Logcat, digite
dessertclicker, que vai filtrar as classesMainActivityeDessertTimer. Observe que, assim que o app é iniciado, o timer também começa a rodar imediatamente.
- Clique no botão Voltar e observe que o timer para novamente. O timer para porque a atividade e o timer que ele controla foram destruídos.
- Use a tela "Recentes" para voltar ao app. No Logcat, o timer é reiniciado do zero.
- Clique no botão Compartilhar. No Logcat, o timer ainda está em execução.

- Clique no botão Página inicial. No Logcat, o timer para de ser executado.
- Use a tela "Recentes" para voltar ao app. No Logcat, o timer começa de novo de onde parou.
- Em
MainActivity, no métodoonStop(), adicione um comentário à chamada destopTimer(). ComentarstopTimer()demonstra o caso em que você inicia uma operação emonStart(), mas se esquece de interrompê-la novamente emonStop(). - Compile e execute o app. Depois, clique no botão "Home" quando o timer começar. Mesmo que o app esteja em segundo plano, o timer está em execução e usando recursos do sistema continuamente. Manter o timer em execução é um vazamento de memória para o app e provavelmente não é o comportamento desejado.
O padrão geral é que, quando você configura ou inicia algo em um callback, você interrompe ou remove esse elemento no callback correspondente. Assim, você evita que algo seja executado quando não for mais necessário.
- Remova a marca de comentário da linha em
onStop()onde você para o timer. - Recorte e cole a chamada
startTimer()deonStart()paraonCreate(). Essa mudança demonstra o caso em que você inicializa e inicia um recurso emonCreate(), em vez de usaronCreate()para inicializar eonStart()para iniciar. - Compile e execute o app. Observe que o timer começa a ser executado, como esperado.
- Clique em "Home" para interromper o app. O timer para de funcionar, como esperado.
- Use a tela Recentes para voltar ao app. Observe que o timer não é iniciado novamente nesse caso, porque
onCreate()só é chamado quando o app é iniciado, não quando ele volta ao primeiro plano.
Pontos importantes:
- Ao configurar um recurso em um callback de ciclo de vida, também desfaça a configuração.
- Faça a configuração e o encerramento nos métodos correspondentes.
- Se você configurar algo em
onStart(), pare ou desfaça novamente emonStop().
No app DessertClicker, é fácil perceber que, se você iniciou o timer em onStart(), precisa pará-lo em onStop(). Só há um timer, então não é difícil lembrar como interromper.
Em um app Android mais complexo, você pode configurar muitas coisas em onStart() ou onCreate() e depois desmontar tudo em onStop() ou onDestroy(). Por exemplo, você pode ter animações, músicas, sensores ou timers que precisam ser configurados e desmontados, iniciados e interrompidos. Se você esquecer um, isso vai causar bugs e dores de cabeça.
A biblioteca Lifecycle, que faz parte do Android Jetpack, simplifica essa tarefa. A biblioteca é especialmente útil quando você precisa rastrear muitas partes móveis, algumas das quais estão em diferentes estados do ciclo de vida. A biblioteca inverte a maneira como os ciclos de vida funcionam: geralmente, a atividade ou o fragmento informa a um componente (como DessertTimer) o que fazer quando ocorre um callback de ciclo de vida. Mas, ao usar a biblioteca de ciclo de vida, o próprio componente monitora as mudanças no ciclo de vida e faz o que é necessário quando elas acontecem.
Há três partes principais da biblioteca Lifecycle:
- Proprietários do ciclo de vida, que são os componentes que têm (e, portanto, "possuem") um ciclo de vida.
ActivityeFragmentsão proprietários de ciclo de vida. Os proprietários de ciclo de vida implementam a interfaceLifecycleOwner. - A classe
Lifecycle, que contém o estado real de um proprietário de ciclo de vida e aciona eventos quando ocorrem mudanças no ciclo de vida. - Observadores do ciclo de vida, que observam o estado do ciclo de vida e realizam tarefas quando ele muda. Os observadores de ciclo de vida implementam a interface
LifecycleObserver.
Nesta tarefa, você vai converter o app DessertClicker para usar a biblioteca de ciclo de vida do Android e aprender como ela facilita o trabalho com os ciclos de vida de atividades e fragmentos do Android.
Etapa 1: transformar o DessertTimer em um LifecycleObserver
Uma parte fundamental da biblioteca do ciclo de vida é o conceito de observação do ciclo de vida. A observação permite que classes (como DessertTimer) saibam sobre o ciclo de vida da atividade ou do fragmento e iniciem e parem por conta própria em resposta a mudanças nesses estados do ciclo de vida. Com um observador de ciclo de vida, você pode remover a responsabilidade de iniciar e interromper objetos dos métodos de atividade e fragmento.
- Abra a classe
DesertTimer.kt. - Mude a assinatura da classe
DessertTimerpara ficar assim:
class DessertTimer(lifecycle: Lifecycle) : LifecycleObserver {Essa nova definição de classe faz duas coisas:
- O construtor usa um objeto
Lifecycle, que é o ciclo de vida que o timer está observando. - A definição de classe implementa a interface
LifecycleObserver.
- Abaixo da variável
runnable, adicione um blocoinità definição da classe. No blocoinit, use o métodoaddObserver()para conectar o objeto de ciclo de vida transmitido do proprietário (a atividade) a essa classe (o observador).
init {
lifecycle.addObserver(this)
}- Adicione a anotação
startTimer()com o@OnLifecycleEvent annotatione use o evento de ciclo de vidaON_START. Todos os eventos de ciclo de vida que seu observador pode observar estão na classeLifecycle.Event.
@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun startTimer() {- Faça o mesmo com
stopTimer(), usando o eventoON_STOP:
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun stopTimer()Etapa 2: modificar a MainActivity
Sua classe MainActivity já é proprietária do ciclo de vida por herança, porque a superclasse FragmentActivity implementa LifecycleOwner. Portanto, não é necessário fazer nada para tornar o ciclo de vida da atividade compatível com o ciclo de vida. Basta transmitir o objeto de ciclo de vida da atividade para o construtor DessertTimer.
- Abra
MainActivity. No métodoonCreate(), modifique a inicialização deDessertTimerpara incluirthis.lifecycle:
dessertTimer = DessertTimer(this.lifecycle)A propriedade lifecycle da atividade contém o objeto Lifecycle a que ela pertence.
- Remova a chamada para
startTimer()emonCreate()e a chamada parastopTimer()emonStop(). Não é mais necessário dizer aoDessertTimero que fazer na atividade, porque ele agora está observando o ciclo de vida e é notificado automaticamente quando o estado do ciclo de vida muda.DessertTimerAgora, tudo o que você faz nesses callbacks é registrar uma mensagem. - Compile e execute o app e abra o Logcat. Observe que o timer começou a ser executado, como esperado.

- Clique no botão home para colocar o app em segundo plano. Observe que o timer parou de funcionar, como esperado.
O que acontece com o app e os dados dele se o Android encerrar o app enquanto ele está em segundo plano? É importante entender esse caso extremo complicado.
Quando seu app passa para o segundo plano, ele não é fechado. Ele apenas é interrompido e espera o usuário retornar. Mas uma das principais preocupações do SO Android é manter a atividade em primeiro plano funcionando sem problemas. Por exemplo, se o usuário estiver usando um app de GPS para ajudar a pegar um ônibus, é importante renderizar esse app rapidamente e continuar mostrando as direções. É menos importante manter o app DessertClicker, que o usuário pode não ter usado há alguns dias, funcionando sem problemas em segundo plano.
O Android regula os apps em segundo plano para que o apliativo em primeiro plano seja executado sem problemas. Por exemplo, o Android limita a quantidade de processamento que os apps em segundo plano podem fazer.
Às vezes, o Android encerra um processo inteiro do app, incluindo todas as atividades associadas a ele. O Android faz esse tipo de encerramento quando o sistema fica sob estresse e corre o risco de sofrer atrasos visuais. Portanto, nenhum callback ou código adicional é executado nesse momento. O processo do app é simplesmente encerrado, de forma silenciosa, em segundo plano. No entanto, para o usuário, não parece que o app foi fechado. Quando o usuário navega de volta para um app que o SO Android encerrou, o Android reinicia esse app.
Nesta tarefa, você vai simular um encerramento de processo do Android e analisar o que acontece com seu app quando ele é iniciado novamente.
Etapa 1: usar o adb para simular o encerramento de um processo
O Android Debug Bridge (adb) é uma ferramenta de linha de comando que permite enviar instruções para emuladores e dispositivos conectados ao computador. Nesta etapa, você vai usar o adb para fechar o processo do app e ver o que acontece quando o Android encerra o app.
- Compile e execute o app. Clique no cupcake algumas vezes.
- Pressione o botão home para colocar o app em segundo plano. O app será interrompido e poderá ser fechado se o Android precisar dos recursos que ele está usando.
- No Android Studio, clique na guia Terminal para abrir o terminal de linha de comando.

- Digite
adbe pressione "Return".
Se você vir muitas saídas que começam comAndroid Debug Bridge version X.XX.Xe terminam comtags to be used by logcat (see logcat —help), está tudo certo. Se você viradb: command not found, verifique se o comandoadbestá disponível no caminho de execução. Para instruções, consulte "Adicionar adb ao seu caminho de execução" no capítulo Utilitários. - Copie e cole este comentário na linha de comando e pressione "Return":
adb shell am kill com.example.android.dessertclickerEsse comando instrui todos os dispositivos ou emuladores conectados a interromper o processo com o nome do pacote dessertclicker, mas apenas se o app estiver em segundo plano. Como o app estava em segundo plano, nada aparece na tela do dispositivo ou emulador para indicar que o processo foi interrompido. No Android Studio, clique na guia Run para ver a mensagem "Application terminated". Clique na guia Logcat para ver que o callback onDestroy() nunca foi executado. Sua atividade simplesmente terminou.
- Use a tela "Recentes" para retornar ao app. Ele aparece nessa tela, mesmo que tenha sido colocado em segundo plano ou parado completamente. Quando você usa a tela de apps recentes para voltar ao app, a atividade é iniciada novamente. A atividade passa por todo o conjunto de callbacks do ciclo de vida de inicialização, incluindo
onCreate(). - Quando o app é reiniciado, ele redefine a "pontuação" (o número de sobremesas vendidas e o total em dólares) para os valores padrão (0). Se o Android encerrou seu app, por que ele não salvou o estado?
Quando o SO reinicia o app, o Android tenta redefinir o app para o estado em que ele estava antes. O Android pega o estado de algumas das suas visualizações e salva em um pacote sempre que você sai da atividade. Alguns exemplos de dados salvos automaticamente são o texto em um EditText (desde que tenha um ID definido no layout) e a pilha de retorno da sua atividade.
No entanto, às vezes, o SO Android não conhece todos os seus dados. Por exemplo, se você tiver uma variável personalizada comorevenueno app DessertClicker, o SO Android não saberá sobre esses dados nem a importância deles para sua atividade. Você precisa adicionar esses dados ao pacote por conta própria.
Etapa 2: usar o onSaveInstanceState() para salvar dados do pacote
O método onSaveInstanceState() é o callback usado para salvar todos os dados necessários caso o SO Android destrua o app. No diagrama de callback do ciclo de vida, o onSaveInstanceState() é chamado depois que a atividade é interrompida. Esse método é chamado toda vez que o app entra em segundo plano.

Pense na chamada onSaveInstanceState() como uma medida de segurança. Ela permite salvar uma pequena quantidade de informações em um pacote quando a atividade sai do primeiro plano. O sistema salva esses dados agora porque, se esperar pelo fechamento do app, poderá estar com menos recursos disponíveis. Salvar os dados todas as vezes garante que os dados atualizados no pacote estejam disponíveis para restauração, se necessário.
- Em
MainActivity, substitua o callbackonSaveInstanceState()e adicione um log statementTimber.
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
Timber.i("onSaveInstanceState Called")
}- Compile e execute o app. Depois, clique no botão home para colocá-lo em segundo plano. Observe que o callback
onSaveInstanceState()ocorre logo após oonPause()e oonStop():
- Adicione estas constantes antes da definição das classes, no topo do arquivo:
const val KEY_REVENUE = "revenue_key"
const val KEY_DESSERT_SOLD = "dessert_sold_key"
const val KEY_TIMER_SECONDS = "timer_seconds_key"Você usará essas chaves para salvar e recuperar os dados do pacote de estado da instância.
- Role para baixo até encontrar
onSaveInstanceState()e observe o parâmetrooutState, que é do tipoBundle.
Um pacote é um conjunto de pares de chave-valor, em que as chaves são sempre strings. É possível colocar valores primitivos, comointeboolean, no pacote.
Como o sistema mantém esse pacote na RAM, uma prática recomendada é guardar poucos dados nele. O tamanho do pacote também é limitado, mas varia de acordo com o dispositivo. Em geral, armazene muito menos do que 100 mil. Caso contrário, seu app poderá falhar com o erroTransactionTooLargeException. - No
onSaveInstanceState(), coloque o valorrevenue(um número inteiro) no pacote usando o métodoputInt():
outState.putInt(KEY_REVENUE, revenue)O método putInt() (e métodos semelhantes da classe Bundle, como putFloat() e putString()) recebem dois argumentos: uma string para a chave (a constante KEY_REVENUE) e o valor real a ser salvo.
- Repita o mesmo processo com o número de sobremesas vendidas e o status do timer:
outState.putInt(KEY_DESSERT_SOLD, dessertsSold)
outState.putInt(KEY_TIMER_SECONDS, dessertTimer.secondsCount)Etapa 3: usar onCreate() para restaurar dados de pacote
- Role para cima até o
onCreate()e examine a assinatura do método:
override fun onCreate(savedInstanceState: Bundle) {O método onCreate() recebe um Bundle sempre que é chamado. Quando a atividade for reiniciada devido ao encerramento de um processo, o pacote que você salvou será transmitido para o onCreate(). Se a atividade estiver sendo iniciada pela primeira vez, esse pacote em onCreate() será null. Portanto, se o pacote não for null, você saberá que está "recriando" a atividade de um ponto conhecido anteriormente.
- Adicione este código a
onCreate(), após a configuração deDessertTimer:
if (savedInstanceState != null) {
revenue = savedInstanceState.getInt(KEY_REVENUE, 0)
}O teste de null determina se há dados no pacote ou se ele é null, o que informa se o app foi iniciado do zero ou recriado depois do encerramento. Esse teste é um padrão comum para restaurar dados do pacote.
A chave usada aqui (KEY_REVENUE) é a mesma que você usou para o putInt(). Para usar sempre a mesma chave, uma prática recomendada é definir essas chaves como constantes. Você usa o método getInt() para extrair dados do pacote, exatamente como usou o putInt() para colocar dados nele. O método getInt() recebe dois argumentos:
- Uma string que funciona como a chave, por exemplo,
"key_revenue"para o valor da receita. - Um valor padrão, caso não exista um para essa chave no pacote.
O número inteiro recebido do pacote é atribuído à variável revenue, e a IU usará esse valor.
- Adicione métodos
getInt()para restaurar o número de sobremesas vendidas e o valor do timer:
if (savedInstanceState != null) {
revenue = savedInstanceState.getInt(KEY_REVENUE, 0)dessertsSold = savedInstanceState.getInt(KEY_DESSERT_SOLD, 0)
dessertTimer.secondsCount =
savedInstanceState.getInt(KEY_TIMER_SECONDS, 0)
}- Compile e execute o app. Pressione o cupcake pelo menos cinco vezes até que ele mude para um donut. Clique em "Home" para colocar o app em segundo plano.
- Na guia Terminal do Android Studio, execute
adbpara encerrar o processo do app.
adb shell am kill com.example.android.dessertclicker- Use a tela "Recentes" para voltar ao app. Desta vez, o app vai retornar com os valores corretos de receita e sobremesas vendidas do pacote. Mas observe que a imagem da sobremesa voltou a ser um cupcake. Só resta mais uma etapa para garantir que o app retorne do encerramento exatamente como quando foi interrompido.
- Na
MainActivity, veja o métodoshowCurrentDessert(). Esse método determina qual imagem de sobremesa precisa ser exibida na atividade com base no número atual de sobremesas vendidas e na lista de sobremesas da variávelallDesserts.
for (dessert in allDesserts) {
if (dessertsSold >= dessert.startProductionAmount) {
newDessert = dessert
}
else break
}Esse método depende do número de sobremesas vendidas para escolher a imagem certa. Portanto, não é necessário fazer nada para armazenar uma referência à imagem no pacote no onSaveInstanceState(). Você já está armazenando o número de sobremesas vendidas nesse pacote.
- No método
onCreate(), no bloco que restaura o estado do pacote, chameshowCurrentDessert():
if (savedInstanceState != null) {
revenue = savedInstanceState.getInt(KEY_REVENUE, 0)
dessertsSold = savedInstanceState.getInt(KEY_DESSERT_SOLD, 0)
dessertTimer.secondsCount =
savedInstanceState.getInt(KEY_TIMER_SECONDS, 0)
showCurrentDessert()
}- Compile e execute o app. Depois, coloque-o em segundo plano. Use
adbpara encerrar o processo. Use a tela "Recentes" para voltar ao app. Agora, os valores das sobremesas vendidas, da receita total e a imagem da sobremesa foram restaurados corretamente.
Há um último caso especial no gerenciamento do ciclo de vida de atividades e fragmentos que é importante entender: como as mudanças de configuração afetam o ciclo de vida das suas atividades e fragmentos.
Uma mudança de configuração acontece 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. 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 for girado do modo retrato para paisagem ou vice-versa), o layout poderá mudar para se ajustar à nova orientação.
Etapa 1: conhecer a rotação do dispositivo e os callbacks do ciclo de vida
- Compile e execute seu app, depois abra o Logcat.
- Gire o dispositivo ou o emulador para o modo paisagem. Você pode girar o emulador para a esquerda ou a direita usando os botões de rotação ou usando as setas e o botão
Control(Commande as teclas de seta no Mac).
- Analise a saída no Logcat. Filtre a saída na
MainActivity.
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. - Em
MainActivity, comente todo o métodoonSaveInstanceState(). - Compile e execute o app novamente. Clique no cupcake algumas vezes e gire o dispositivo ou emulador. Desta vez, quando o dispositivo é girado e a atividade é encerrada e recriada, ela é iniciada com valores padrão.
Quando uma mudança de configuração ocorre, o Android usa o mesmo pacote de estado da instância que você aprendeu na tarefa anterior para salvar e restaurar o estado do app. Assim como em um encerramento de processo, useonSaveInstanceState()para colocar os dados do app no pacote. Em seguida, restaure os dados emonCreate()para evitar a perda de dados de estado da atividade se o dispositivo for girado. - Em
MainActivity, remova o comentário do métodoonSaveInstanceState(), execute o app, clique no cupcake e gire o app ou dispositivo. Desta vez, os dados de sobremesa são mantidos na rotação da atividade.
Projeto do Android Studio: DessertClickerFinal (link em inglês)
Dicas sobre o ciclo de vida
- Se você configurar ou iniciar algo em um callback de ciclo de vida, pare ou remova esse item no callback correspondente. Ao parar o dispositivo, você garante que ele não continue funcionando quando não for mais necessário. Por exemplo, se você configurar um timer em
onStart(), precisará pausar ou parar o timer emonStop(). - Use
onCreate()apenas para inicializar as partes do app que são executadas uma vez, quando o app é iniciado. UseonStart()para iniciar as partes do app que são executadas quando ele é iniciado e sempre que volta ao primeiro plano.
Biblioteca do ciclo de vida
- Use a biblioteca de ciclo de vida do Android para transferir o controle do ciclo de vida da atividade ou do fragmento para o componente real que precisa ter reconhecimento do ciclo de vida.
- Os proprietários do ciclo de vida são componentes que têm (e, portanto, "possuem") ciclos de vida, incluindo
ActivityeFragment. Os proprietários de ciclo de vida implementam a interfaceLifecycleOwner. - Os observadores do ciclo de vida prestam atenção ao estado atual do ciclo de vida e realizam tarefas quando ele muda. Os observadores de ciclo de vida implementam a interface
LifecycleObserver. - Os objetos
Lifecyclecontêm os estados reais do ciclo de vida e acionam eventos quando o ciclo de vida muda.
Para criar uma classe compatível com o ciclo de vida:
- Implemente a interface
LifecycleObserverem classes que precisam ser compatíveis com o ciclo de vida. - Inicialize uma classe observadora de ciclo de vida com o objeto de ciclo de vida da atividade ou do fragmento.
- Na classe de observador do ciclo de vida, anote os métodos compatíveis com o ciclo de vida com a mudança de estado do ciclo de vida em que eles estão interessados.
Por exemplo, a anotação@OnLifecycleEvent(Lifecycle.Event.ON_START)indica que o método está monitorando o evento do ciclo de vidaonStart.
Encerramento de processos e salvamento do estado da atividade
- O Android regula os apps em segundo plano para que o app em primeiro plano seja executado sem problemas. Essa regulamentação inclui limitar a quantidade de processamento que os apps em segundo plano podem fazer e, às vezes, até mesmo encerrar todo o processo do app.
- O usuário não consegue saber se o sistema desligou um app em segundo plano. O app ainda aparece na tela de apps recentes e é reiniciado no mesmo estado em que o usuário o deixou.
- O Android Debug Bridge (
adb) é uma ferramenta de linha de comando que permite enviar instruções para emuladores e dispositivos conectados ao computador. Useadbpara simular o encerramento de um processo no app. - Quando o Android encerra o processo do app, o método de ciclo de vida
onDestroy()não é chamado. O app simplesmente para.
Como preservar o estado de atividades e fragmentos
- Quando seu app for movido para o segundo plano, pouco depois de
onStop()ser chamado, os dados do app serão salvos em um pacote. Alguns dados, como o conteúdo de umEditText, são salvos automaticamente. - O pacote é uma instância de
Bundle, que é uma coleção de chaves e valores. As chaves são sempre strings. - Use o callback
onSaveInstanceState()para salvar outros dados no pacote que você quer guardar, mesmo que o app tenha sido encerrado automaticamente. Para colocar dados nele, use os métodos do pacote que começam comput, comoputInt(). - Você pode acessar os dados do pacote no método
onRestoreInstanceState()ou, geralmente, emonCreate(). O métodoonCreate()tem um parâmetrosavedInstanceStateque armazena o pacote. - Se a variável
savedInstanceStatecontivernull, a atividade foi iniciada sem um pacote de estado e não há dados de estado a serem recuperados. - Para recuperar dados do pacote com uma chave, use os métodos do
Bundleque começam comget, comogetInt().
Mudanças de configuração
- Uma mudança de configuração acontece quando o estado do dispositivo muda de forma tão radical que a maneira mais fácil do sistema resolver a mudança é encerrar 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 mudar ou um teclado físico for conectado.
- Quando uma mudança de configuração ocorre, o Android invoca todos os callbacks de desligamento do ciclo de vida da atividade. Em seguida, 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 o pacote de estado disponível para o método
onCreate(). - Como no caso do encerramento do processo, salve o estado do app no pacote no método
onSaveInstanceState().
Curso da Udacity:
Documentação do desenvolvedor Android:
- Atividades (guia da API)
Activity(referência da API)- Entenda o ciclo de vida da atividade
- Como gerenciar ciclos de vida com componentes que os reconhecem
LifecycleOwnerLifecycleLifecycleObserveronSaveInstanceState()- Gerenciar mudanças de configuração
- Como salvar estados da IU
Outro:
- Timber (GitHub)
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.
Mudar um app
Abra o app DiceRoller da lição 1. Baixe o app aqui se você ainda não tiver feito isso. Compile e execute o app. Observe que, se você girar o dispositivo, o valor atual dos dados será perdido. Implemente onSaveInstanceState() para reter esse valor no pacote e restaure-o em onCreate().
Responda estas perguntas
Pergunta 1
Seu app contém uma simulação física que exige cálculos complexos para ser mostrada. Em seguida, o usuário recebe uma ligação. Qual destas afirmativas é verdadeira?
- Durante a ligação, continue calculando as posições dos objetos na simulação física.
- Durante a ligação, pare de calcular as posições dos objetos na simulação física.
Pergunta 2
Que método do ciclo de vida precisa ser substituído para pausar a simulação quando o app não está na tela?
onDestroy()onStop()onPause()onSaveInstanceState()
Pergunta 3
Para tornar uma classe compatível com o ciclo de vida usando a biblioteca Lifecycle do Android, qual interface a classe precisa implementar?
LifecycleLifecycleOwnerLifecycle.EventLifecycleObserver
Pergunta 4
Em quais circunstâncias o método onCreate() na sua atividade recebe um Bundle com dados (ou seja, o Bundle não é null)? Mais de uma resposta pode ser válida.
- Se a atividade for reiniciada depois que o dispositivo for girado.
- Se a atividade for iniciada do zero.
- Se a atividade for retomada depois de retornar do segundo plano.
- Se o dispositivo for reinicializado.
Comece a próxima lição:
Para acessar links de outros codelabs neste curso, consulte a página inicial dos codelabs de conceitos básicos do Kotlin para Android.