Este codelab faz parte do curso Conceitos básicos do Kotlin para Android. Você aproveitará mais o curso se fizer os codelabs em sequência. Todos os codelabs do curso estão listados na página de destino dos codelabs do curso Conceitos básicos do Kotlin para Android.
Introdução
No último codelab, você aprendeu sobre os ciclos de vida de Activity
e Fragment
e explorou os métodos chamados quando o estado do ciclo de vida muda em atividades e fragmentos. Neste codelab, você vai explorar o ciclo de vida da atividade em mais detalhes. Você também aprenderá sobre a biblioteca Lifecycle do Android Jetpack, que pode ajudar a gerenciar eventos de ciclo de vida com códigos mais organizados e fáceis 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
Activity
eFragment
e 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ê aprenderá
- Como configurar, iniciar e interromper partes do app nos callbacks do ciclo de vida.
- Como usar a biblioteca Android lifecycle para criar um observador do 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 e como salvar e restaurar esses dados automaticamente quando o Android é fechado.
- 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 interrompa esse timer várias vezes no ciclo de vida da atividade.
- Modifique o app para usar a biblioteca Lifecycle do Android e converta a classe
DessertTimer
em um observador do ciclo de vida. - Configure e use o Android Debug Bridge (
adb
) para simular o encerramento do processo do app e os callbacks de ciclo de vida que ocorrem em seguida. - Implemente o método
onSaveInstanceState()
para reter os dados que podem ser perdidos se o app for fechado inesperadamente. Como adicionar um código para restaurar esses dados quando o app for iniciado novamente.
Neste codelab, você expandirá o app DessertClicker do codelab anterior. Adicione um timer em segundo plano e converta o app para usar a biblioteca Android lifecycle.
No codelab anterior, você aprendeu a observar os ciclos de vida de atividades e fragmentos substituindo vários callbacks do ciclo de vida e registrando quando o sistema invoca esses callbacks. Nesta tarefa, você verá um exemplo mais complexo de gerenciamento de tarefas do ciclo de vida no app DessertClicker. Você usa um timer que exibe um log statement a cada segundo, com a contagem de segundos de execução.
Etapa 1: configurar o DessertTimer
- Abra o app DessertClicker do último codelab. Você pode fazer o download do DessertClickerLogs aqui se não tiver o app.
- Na visualização Project, expanda java > com.example.android.dessertclicker e abra
DessertTimer.kt
. Observe que, no momento, todo o código é comentado e, por isso, 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+/
no Mac). Esse comando remove todo o comentário do código no arquivo. O Android Studio pode exibir erros de referência não resolvidos até você recriar o aplicativo. - A classe
DessertTimer
incluistartTimer()
estopTimer()
, que iniciam e interrompem o timer. Quando ostartTimer()
está em execução, o timer imprime uma mensagem de registro a cada segundo, com a contagem total de segundos em que o tempo foi executado. O métodostopTimer()
, por sua vez, interrompe o timer e os log statements.
- Abra o
MainActivity.kt
Na parte superior da classe, logo abaixo da variáveldessertsSold
, adicione uma variável para o timer:
private lateinit var dessertTimer : DessertTimer;
- Role para baixo até encontrar
onCreate()
e crie um novo objetoDessertTimer
, logo após a chamada parasetOnClickListener()
:
dessertTimer = DessertTimer()
Agora que você tem um objeto de timer de sobremesa, considere por onde começar e interromper o timer para que ele seja executado somente quando a atividade estiver na tela. Veja algumas opções nas próximas etapas.
Etapa 2: iniciar e parar o timer
O método onStart()
é chamado pouco antes de a atividade se tornar visível. O método onStop()
é chamado depois que a atividade deixa de ser visível. Esses callbacks parecem bons candidatos para quando 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 filtrará pelas classesMainActivity
eDessertTimer
. Quando o app for iniciado, o temporizador também será iniciado imediatamente. - Clique no botão Voltar e observe que o timer é interrompido novamente. O timer é interrompido porque a atividade e o timer que ele controla foram destruídos.
- Use a tela Recentes para retornar ao app. No Logcat, o timer é reiniciado em 0.
- Clique no botão Compartilhar. No Logcat, o timer ainda está em execução.
- Clique no botão Home. No Logcat, o timer para de funcionar.
- Use a tela Recentes para retornar ao app. No Logcat, o timer inicia novamente de onde parou.
- No método
onStop()
doMainActivity
, comente a chamada parastopTimer()
. Ao comentarstopTimer()
, demonstramos o caso em que você inicia uma operação emonStart()
, mas se esquece de interrompê-la novamente emonStop()
. - Compile e execute o app. Depois disso, clique no botão home. Embora o app esteja em segundo plano, o temporizador está em execução e continua usando os recursos do sistema. Fazer o timer continuar sendo executado é um vazamento de memória para seu app, e provavelmente não é o comportamento desejado.
O padrão geral é que, ao configurar ou iniciar algo em um callback, você para ou remove esse gatilho no callback correspondente. Dessa forma, você evita ter que executar nada quando ele não for mais necessário.
- Remova o comentário da linha da
onStop()
, em que você interrompe 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 inicializá-lo eonStart()
para iniciá-lo. - Compile e execute o app. Observe que o timer começa a ser executado como esperado.
- Clique em "Início" para interromper o app. O timer para de funcionar, como esperado.
- Use a tela Recentes para retornar ao app. Nesse caso, o timer não será reiniciado, porque
onCreate()
será chamado apenas quando o app for iniciado. Ele não é chamado quando um app volta para o primeiro plano.
Pontos importantes:
- Ao configurar um recurso em um callback do ciclo de vida, também desative o recurso.
- configurar e desmontar nos métodos correspondentes.
- Se você configurar algo em
onStart()
, pare ou desmonte-o novamente emonStop()
.
No app DessertClicker, é bem fácil ver que, se você iniciou o timer em onStart()
, precisa interrompê-lo no onStop()
. Como há apenas um timer, parar é difícil de lembrar.
Em um app Android mais complexo, é possível configurar muitas coisas em onStart()
ou onCreate()
e, em seguida, desmontar tudo em onStop()
ou onDestroy()
. Por exemplo, pode ser necessário configurar, desmontar e iniciar animações e sensores de música, sensores ou timers. Se você esquecer um deles, isso pode causar bugs e dores de cabeça.
A biblioteca Lifecycle, que faz parte do Android Jetpack, simplifica essa tarefa. A biblioteca é útil principalmente nos casos em que você precisa acompanhar muitas partes móveis, algumas delas em diferentes estados do ciclo de vida. A biblioteca gira a forma como os ciclos de vida funcionam: geralmente, a atividade ou o fragmento informa a um componente (como DessertTimer
) o que fazer quando um callback de ciclo de vida ocorre. No entanto, quando você usa a biblioteca de ciclo de vida, o componente em si observa mudanças no ciclo de vida e, em seguida, faz o que é necessário quando essas mudanças acontecem.
Há três partes principais da biblioteca do ciclo de vida:
- Os proprietários do ciclo de vida, que são os componentes que têm (e, portanto, "próprios") um ciclo de vida.
Activity
eFragment
são proprietários do ciclo de vida. Os proprietários do ciclo de vida implementam a interfaceLifecycleOwner
. - A classe
Lifecycle
, que armazena o estado real de um proprietário do ciclo de vida e aciona eventos quando ocorrem mudanças no ciclo de vida. - Observadores de ciclo de vida, que observam o estado do ciclo de vida e realizam tarefas quando o ciclo de vida muda. Observadores do ciclo de vida implementam a interface
LifecycleObserver
.
Nesta tarefa, você converterá o app DessertClicker para usar a biblioteca Android lifecycle e aprenderá como a biblioteca facilita o trabalho com a atividade do Android e os ciclos de vida de fragmentos.
Etapa 1: transformar o DessertTimer em um LifecycleObserver
Uma parte essencial da biblioteca de 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 de atividades ou fragmentos, além de iniciar e parar por conta própria em resposta a mudanças nesses estados do ciclo. Com um observador do ciclo de vida, você pode eliminar a responsabilidade de iniciar e parar objetos dos métodos de atividade e fragmento.
- Abra a classe
DesertTimer.kt
. - Mude a assinatura da classe
DessertTimer
para que ela fique assim:
class DessertTimer(lifecycle: Lifecycle) : LifecycleObserver {
Essa nova definição de classe tem duas funções:
- O construtor usa um objeto
Lifecycle
, que é o ciclo de vida que o temporizador está observando. - A definição da 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 do ciclo de vida transmitido do proprietário (a atividade) a essa classe (o observador).
init {
lifecycle.addObserver(this)
}
- Adicione a anotação
@OnLifecycleEvent annotation
àstartTimer()
e use o evento de ciclo de vidaON_START
. Todos os eventos do ciclo de vida que o observador do ciclo de vida pode observar estão na classeLifecycle.Event
.
@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun startTimer() {
- Faça o mesmo para
stopTimer()
usando o eventoON_STOP
:
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun stopTimer()
Etapa 2: modificar a MainActivity
A classe MainActivity
já é uma proprietária de ciclo de vida pela herança, porque a superclasse FragmentActivity
implementa LifecycleOwner
. Portanto, você não precisa fazer nada para tornar sua atividade consciente do ciclo de vida. Tudo o que você precisa fazer é transmitir o objeto do ciclo de vida da atividade para o construtor do DessertTimer
.
- Abra o
MainActivity
No métodoonCreate()
, modifique a inicialização daDessertTimer
para incluir athis.lifecycle
:
dessertTimer = DessertTimer(this.lifecycle)
A propriedade lifecycle
da atividade detém o objeto Lifecycle
que essa atividade possui.
- Remova a chamada para
startTimer()
noonCreate()
e a chamada parastopTimer()
noonStop()
. Você não precisa mais dizer aoDessertTimer
o que fazer na atividade, porque oDessertTimer
agora está observando o ciclo de vida e será notificado automaticamente quando o estado do ciclo de vida mudar. Agora você só precisa registrar uma mensagem nesses callbacks. - Compile e execute o app. Depois, 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 esse app enquanto estiver em segundo plano? É importante entender esse caso complicado.
Quando o app fica em segundo plano, ele não é destruído, só para e aguarda o usuário retornar. No entanto, uma das principais preocupações do SO Android é manter a atividade em primeiro plano sem problemas. Por exemplo, se o usuário estiver usando um app de GPS para ajudá-lo a pegar um ônibus, será importante renderizar esse app rapidamente e continuar mostrando as rotas. É menos importante manter o app DessertClicker, que o usuário pode não ter visto por alguns dias, sendo executado sem problemas em segundo plano.
O Android regula os apps em segundo plano para que o app 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 até encerra um processo inteiro do app, o que inclui 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 navegar de volta para um app que o SO Android encerrou, o Android reiniciará esse app.
Nesta tarefa, você simulará um encerramento de processo do Android e examinará o que acontece com o app quando ele for reiniciado.
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 seu computador. Nesta etapa, você usa o adb
para fechar o processo do seu app e ver o que acontece quando ele é encerrado pelo Android.
- Compile e execute seu app. Clique no cupcake algumas vezes.
- Pressione o botão home para colocar o app em segundo plano. Seu app será interrompido, e estará sujeito a 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
adb
e pressione Enter.
Se você vir um grande volume de saída que começa comAndroid Debug Bridge version X.XX.X
e termina comtags to be used by logcat (see logcat —h
elp, não tem problema. Se, em vez disso, você viradb: command not found
, verifique se o comandoadb
está disponível no seu caminho de execução. Para ver 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 Enter:
adb shell am kill com.example.android.dessertclicker
Esse comando instrui os dispositivos ou emuladores conectados a interromper o processo com o nome do pacote dessertclicker
, mas somente se o app estiver em segundo plano. Como seu app estava em segundo plano, nada é exibido na tela do dispositivo ou emulador para indicar que o processo foi interrompido. No Android Studio, clique na guia Run para ver uma mensagem que diz "quoquo;Aplicativo encerrado"." Clique na guia Logcat para ver se o callback onDestroy()
nunca foi executado. Sua atividade simplesmente terminou.
- Use a tela Recentes para retornar ao app. Seu app aparecerá em recentes, mesmo que tenha sido colocado em segundo plano ou interrompido completamente. Quando você usa a tela Recentes para retornar ao app, a atividade é iniciada novamente. A atividade passa por todo o conjunto de callbacks do ciclo de vida da inicialização, incluindo
onCreate()
. - Quando o app for reiniciado, ele redefinirá sua pontuação (quociente), o número de sobremesas vendidas e o total de dólares, para os valores padrão (0). Se o Android encerrou seu app, por que não salvou seu estado?
Quando o SO reinicia o app para você, o Android faz o possível para redefinir o app para o estado anterior. O Android salva o estado de algumas das suas visualizações e o salva em um pacote sempre que você sai da atividade. Alguns exemplos de dados que são salvos automaticamente são o texto de um EditText (desde que eles tenham um ID definido no layout) e a pilha de retorno da sua atividade.
No entanto, às vezes o SO Android não sabe sobre todos os seus dados. Por exemplo, se você tiver uma variável personalizada comorevenue
no app DessertClicker, o SO Android não saberá sobre esses dados ou 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 do 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 de atualização do 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. 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, comoint
eboolean
, no pacote.
Como o sistema mantém esse pacote na RAM, é uma prática recomendada manter os dados nele pequenos. O tamanho do pacote também é limitado, mas varia de acordo com o dispositivo. Geralmente, você precisa armazenar menos de 100 mil. Caso contrário, há o risco de o app falhar devido ao erroTransactionTooLargeException
. - Em
onSaveInstanceState()
, coloque o valorrevenue
(um número inteiro) no pacote com 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 o 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, este pacote no 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 à
onCreate()
, após a configuração doDessertTimer
:
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 "Início" para colocar o app em segundo plano.
- Na guia Terminal do Android Studio, execute
adb
para encerrar o processo do app.
adb shell am kill com.example.android.dessertclicker
- Use a tela Recentes para retornar ao app. Desta vez, o app retornará com a receita correta e os valores vendidos de sobremesas do pacote. Mas observe que a imagem da sobremesa voltou a ser um cupcake. Falta fazer mais uma coisa para garantir que o app retorne do modo de desligamento exatamente como estava.
- 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.
- Em
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 e coloque-o em segundo plano. Use
adb
para encerrar o processo. Use a tela Recentes para retornar ao app. Os valores das sobremesas, a 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 dos seus 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 mudar o idioma do dispositivo, todo o layout precisará ser alterado para acomodar diferentes direções de texto. Se o usuário conectar o dispositivo a uma base ou adicionar um teclado físico, o layout do app poderá 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: explorar 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
(Command
e 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. - No
MainActivity
, comente todo o métodoonSaveInstanceState()
. - Compile e execute seu app novamente. Clique no cupcake algumas vezes e gire o dispositivo ou emulador. Dessa vez, quando o dispositivo for girado e a atividade for encerrada e recriada, ela será iniciada com valores padrão.
Quando ocorre uma mudança de configuração, 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. Como no encerramento de um processo, useonSaveInstanceState()
para colocar os dados do app no pacote. Em seguida, restaure os dados noonCreate()
para evitar a perda de dados do estado da atividade caso a rotação do dispositivo seja feita. - No
MainActivity
, remova a marca de comentário do métodoonSaveInstanceState()
, execute o app, clique no cupcake e gire o app ou dispositivo. Desta vez, os dados de sobremesas são retidos na rotação da atividade.
Projeto do Android Studio: DessertClickerFinal
Dicas do ciclo de vida
- Se você configurar ou iniciar um retorno de chamada do ciclo de vida, pare ou remova esse elemento do callback correspondente. Ao interromper o processo, você garante que ele não continuará em execução quando não for mais necessário. Por exemplo, se você configurar um timer em
onStart()
, será necessário pausar ou parar o timer emonStop()
. - Use
onCreate()
somente para inicializar as partes do app que são executadas uma vez, quando ele é iniciado. UseonStart()
para iniciar as partes do app que são executadas quando ele é iniciada e sempre que ele retorna ao primeiro plano.
Biblioteca Lifecycle
- Use a biblioteca Android lifecycle para mudar o controle do ciclo de vida da atividade ou do fragmento para o componente real que precisa ser compatível com o ciclo de vida.
- Os proprietários do ciclo de vida são componentes que têm (e, portanto, "próprios) ciclos de vida, incluindo
Activity
eFragment
. Os proprietários do 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 o ciclo de vida muda. Observadores do ciclo de vida implementam a interface
LifecycleObserver
. - Os objetos
Lifecycle
contêm os estados reais do ciclo de vida e acionam eventos quando o ciclo de vida é modificado.
Para criar uma classe com reconhecimento de ciclo de vida:
- Implemente a interface
LifecycleObserver
em classes que precisam reconhecer o ciclo de vida. - Inicialize uma classe de observador do ciclo de vida com o objeto de ciclo de vida da atividade ou do fragmento.
- Na classe do observador do ciclo de vida, anote os métodos com reconhecimento de ciclo de vida com a mudança de estado do ciclo de vida em que estão interessados.
Por exemplo, a anotação@OnLifecycleEvent(Lifecycle.Event.ON_START)
indica que o método está assistindo ao eventoonStart
do ciclo de vida.
Processar encerramentos e salvar o estado da atividade
- O Android regula os apps em execução em segundo plano para que o app em primeiro plano seja executado sem problemas. Essa regulamentação inclui a limitação da quantidade de processamento que os apps em segundo plano podem fazer e, às vezes, o encerramento de todo o processo de apps.
- O usuário não consegue saber se o sistema encerrou um app em segundo plano. O app ainda aparecerá na tela Recentes e será reiniciado no mesmo estado em que o usuário o fechou.
- O Android Debug Bridge (
adb
) é uma ferramenta de linha de comando que permite enviar instruções para emuladores e dispositivos conectados ao seu computador. Você pode usar oadb
para simular um encerramento de processo no seu app. - Quando o Android encerra o processo do app, o método de ciclo de vida
onDestroy()
não é chamado. O app será interrompido.
Como preservar a atividade e o estado do fragmento
- 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âmetrosavedInstanceState
que armazena o pacote. - Se a variável
savedInstanceState
contivernull
, 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
Bundle
que 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 é desligar 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:
- Como desenvolver apps Android com Kotlin (link em inglês)
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
LifecycleOwner
Lifecycle
LifecycleObserver
onSaveInstanceState()
- Gerenciar mudanças de configuração
- Como salvar estados da IU
Outro:
- Timber (GitHub)
Esta seção lista as possíveis atividades para os alunos que estão trabalhando neste codelab como parte de um curso ministrado por um instrutor. Cabe ao instrutor fazer o seguinte:
- Se necessário, atribua o dever de casa.
- Informe aos alunos como enviar o dever de casa.
- Atribua nota aos trabalhos de casa.
Os professores podem usar essas sugestões o quanto quiserem, e eles devem se sentir à vontade para passar o dever de casa como achar adequado.
Se você estiver fazendo este codelab por conta própria, use essas atividades para testar seu conhecimento.
Mudar um app
Abra o app DiceRoller da Lição 1. Se você não tiver o app instalado, faça o download dele aqui. Compile e execute o app. Observe que, se você girar o dispositivo, o valor atual do dado será perdido. Implemente onSaveInstanceState()
para manter esse valor no pacote e restaure esse valor em onCreate()
.
Responda a estas perguntas
Pergunta 1
Seu app contém uma simulação de física que requer computação avançada para ser exibida. Em seguida, o usuário recebe uma chamada telefônica. Qual destas afirmativas é verdadeira?
- Durante a chamada telefônica, continue calculando as posições dos objetos na simulação física.
- Durante a chamada telefônica, 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 consciente do ciclo de vida com a biblioteca de ciclo de vida do Android, qual interface a classe precisa implementar?
Lifecycle
LifecycleOwner
Lifecycle.Event
LifecycleObserver
Pergunta 4
Em que circunstâncias o método onCreate()
na sua atividade recebe um Bundle
com dados (ou seja, Bundle
não é null
)? Mais de uma resposta pode ser aplicada.
- A atividade é reiniciada após a rotação do dispositivo.
- A atividade será iniciada do zero.
- A atividade é retomada depois de ser retornada em segundo plano.
- Se o dispositivo for reinicializado.
Inicie a próxima lição:
Para ver links de outros codelabs neste curso, consulte a página de destino dos codelabs do curso Conceitos básicos do Kotlin para Android.