No último tutorial, você aprendeu sobre os ciclos de vida Activity
e Fragment
, e
explorou os métodos que são chamados quando o estado do ciclo de vida muda em atividades e fragmentos. Neste
tutorial, você explora o ciclo de vida da atividade com mais detalhes. Você também aprenderá sobre a biblioteca
de ciclo de vida do Android Jetpack, que pode ajudá-lo a gerenciar eventos de ciclo de vida com um código mais
organizado e fácil de manter.
Activity
e Fragment
e os retornos de chamada que são
invocados quando uma atividade se move entre estados.onCreate()
e
onStop()
para realizar operações em momentos diferentes no ciclo de vida da atividade ou
fragmento.DessertTimer
em um observador de ciclo de vida. adb
) para simular o desligamento do processo do seu
aplicativo e os retornos de chamada do ciclo de vida que ocorrem em seguida. onSaveInstanceState()
para reter os dados do aplicativo que podem ser
perdidos se o aplicativo for fechado inesperadamente. Adicione o código para restaurar os dados quando o
aplicativo for iniciado novamente.Neste tutorial, você expande o aplicativo DessertClicker do tutorial anterior. Você adiciona um cronômetro de plano de fundo e converte o aplicativo para usar a biblioteca de ciclo de vida do Android.
No tutorial anterior, você aprendeu como observar a atividade e os ciclos de vida do fragmento substituindo vários retornos de chamada do ciclo de vida e registrando quando o sistema invoca esses retornos de chamada. Nesta tarefa, você explorará um exemplo mais complexo de gerenciamento de tarefas de ciclo de vida no aplicativo DessertClicker. Você usa um cronômetro que imprime uma instrução de registro a cada segundo, com a contagem do número de segundos que está em execução.
DessertTimer.kt
. Observe que agora todo o código está comentado, portanto, não é executado
como parte do aplicativo. Control+/
(Command+/
em um Mac). Este comando descomenta todo o código
no arquivo. (O Android Studio pode mostrar erros de referência não resolvidos até que você reconstrua o
aplicativo).DessertTimer
inclui startTimer()
e stopTimer()
,
que iniciam e param o cronômetro. Quando startTimer()
está em execução, o cronômetro imprime uma
mensagem de registro a cada segundo, com a contagem total de segundos em que o tempo está decorrendo. O método
stopTimer()
, por sua vez, para o cronômetro e as instruções de log.MainActivity.kt
. No topo da classe, logo abaixo da variável dessertsSold
,
adicione uma variável para o cronômetro: private lateinit var dessertTimer : DessertTimer;
onCreate()
e crie um objeto DessertTimer
, logo após a chamada
para setOnClickListener()
:dessertTimer = DessertTimer()
Agora que você tem um objeto cronômetro de sobremesa, considere onde você deve iniciar e parar o cronômetro
para fazê-lo funcionar only quando a atividade estiver na tela. Você verá algumas opções nas
próximas etapas.
O método onStart()
é chamado antes que a atividade se torne visível. O método
onStop()
é chamado depois que a atividade deixa de ser visível. Esses retornos de chamada parecem
bons candidatos para quando iniciar e parar o cronômetro.
MainActivity
, inicie o cronômetro no retorno de chamada onStart()
: override fun onStart() {
super.onStart()
dessertTimer.startTimer()
Timber.i("onStart called")
}
onStop()
: override fun onStop() {
super.onStop()
dessertTimer.stopTimer()
Timber.i("onStop Called")
}
dessertclicker
, que filtrará pelas classes MainActivity
e
DessertTimer
. Observe que, assim que o aplicativo é iniciado, o cronômetro também começa a
funcionar imediatamente.MainActivity
, no método onStop()
, comente a chamada para
stopTimer()
. Comentando stopTimer()
demonstra o caso em que você inicia uma operação
em onStart()
, mas se esquece de interrompê-la novamente em onStop()
. onStop()
onde você para o cronômetro.startTimer()
de onStart()
para onCreate()
.
Esta mudança demonstra o caso em que você inicializa e inicia um recurso em onCreate()
, em vez de
usar onCreate()
para inicializá-lo e onStart()
para iniciá-lo. onCreate()
somente é chamado quando o aplicativo é iniciado - não é
chamado quando um aplicativo retorna ao primeiro plano. Pontos principais a serem lembrados:
onStart()
, pare ou desmonte novamente em
onStop()
.No aplicativo DessertClicker, é bastante fácil ver que, se você iniciar o cronômetro em onStart()
,
será necessário interromper o cronômetro em onStop()
. Há apenas um cronômetro, portanto, pará-lo
não é difícil de lembrar.
Em um aplicativo Android mais complexo, você pode configurar muitos elementos em onStart()
ou
onCreate()
e, em seguida, removê-los todos em onStop()
ou onDestroy()
.
Por exemplo, você pode ter animações, música, sensores ou cronômetros que você precisa para configurar e
derrubar, e iniciar e parar. Se você esquecer um, isso leva a erros e dores de cabeça.
A biblioteca de ciclo de vida, que faz parte do Android Jetpack, simplifica essa tarefa. A biblioteca é especialmente útil nos casos em
que você precisa rastrear muitas peças móveis, algumas das quais estão em diferentes estados de ciclo de vida. A
biblioteca muda a maneira como os ciclos de vida funcionam: Normalmente, a atividade ou fragmento diz a um
componente (como DessertTimer
) o que fazer quando ocorre um retorno de chamada do ciclo de vida.
Mas quando você usa a biblioteca de ciclo de vida, o próprio componente observa as mudanças do ciclo de vida e
faz o que é necessário quando essas mudanças acontecem.
Existem três partes principais da biblioteca de ciclo de vida:
Activity
e Fragment
são proprietários do ciclo de vida. Os proprietários do ciclo de
vida implementam a interface LifecycleOwner
.Lifecycle
, que mantém o estado real de um proprietário de ciclo de vida e dispara
eventos quando ocorrem mudanças no ciclo de vida. LifecycleObserver
. Nesta tarefa, você converte o aplicativo DessertClicker para usar a biblioteca de ciclo de vida do Android e aprende como a biblioteca torna o trabalho com a atividade do Android e os ciclos de vida de fragmento mais fáceis de gerenciar.
Uma parte importante da biblioteca do ciclo de vida é o conceito de observação do ciclo de vida. A
observação permite que as classes (como DessertTimer
) saibam sobre a atividade ou ciclo de vida do
fragmento, e iniciem e parem em resposta às mudanças nesses estados do ciclo de vida. Com um observador de ciclo
de vida, você pode remover a responsabilidade de iniciar e parar objetos dos métodos de atividade e fragmento.
DesertTimer.kt
. DessertTimer
para ficar assim:class DessertTimer(lifecycle: Lifecycle) : LifecycleObserver {
Esta nova definição de classe faz duas tarefas:
Lifecycle
, que é o ciclo de vida que o cronômetro está observando.
LifecycleObserver
. runnable
, adicione um bloco init
à definição da classe. No
bloco init
, use o método addObserver()
para conectar o objeto de ciclo de vida
passado do proprietário (a atividade) a esta classe (o observador). init {
lifecycle.addObserver(this)
}
startTimer()
com a anotação @OnLifecycleEvent annotation
e use o evento de
ciclo de vida ON_START
. Todos os eventos de ciclo de vida que seu observador de ciclo de vida
pode observar estão na classe Lifecycle.Event
.@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun startTimer() {
stopTimer()
, usando o evento ON_STOP
:@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun stopTimer()
Sua classe MainActivity
já é um proprietário do ciclo de vida por meio de herança, pois a
superclasse FragmentActivity
implementa LifecycleOwner
. Portanto, não há nada que você
precise fazer para tornar sua atividade consciente do ciclo de vida. Tudo o que você precisa fazer é passar o
objeto de ciclo de vida da atividade para o construtor DessertTimer
.
MainActivity
. No método onCreate()
, modifique a inicialização de
DessertTimer
para incluir this.lifecycle
: dessertTimer = DessertTimer(this.lifecycle)
A propriedade lifecycle
da atividade contém o objeto Lifecycle
que esta atividade
possui.
startTimer()
em onCreate()
, e a chamada para
stopTimer()
em onStop()
. Você não precisa mais dizer ao DessertTimer
o
que fazer na atividade, pois o DessertTimer
agora está observando o próprio ciclo de vida e é
notificado automaticamente quando o estado do ciclo de vida muda. Tudo o que você faz nesses retornos de
chamada agora é registrar uma mensagem.
O que acontecerá com seu aplicativo e seus dados se o Android encerrar o aplicativo enquanto ele estiver em segundo plano? Esse caso complicado é importante entender.
Quando seu aplicativo vai para o segundo plano, ele não é destruído, ele apenas é interrompido e aguarda o retorno do usuário. Mas uma das principais preocupações do sistema operacional Android é manter a atividade que está em primeiro plano funcionando sem problemas. Por exemplo, se o seu usuário estiver usando um aplicativo GPS para ajudá-lo a pegar um ônibus, é importante renderizar esse aplicativo GPS rapidamente e continuar mostrando as direções. É menos importante manter o aplicativo DessertClicker, que o usuário pode não ver por alguns dias, funcionando perfeitamente em segundo plano.
O Android regula os aplicativos em segundo plano para que o aplicativo em primeiro plano possa ser executado sem problemas. Por exemplo, o Android limita a quantidade de processamento que os aplicativos executados em segundo plano podem fazer.
Às vezes, o Android até desliga todo o processo do aplicativo, que inclui todas as atividades associadas ao aplicativo. O Android faz esse tipo de desligamento quando o sistema está estressado e corre o risco de atrasar visualmente, portanto, nenhum retorno de chamada ou código adicional é executado neste ponto. O processo do seu aplicativo é simplesmente encerrado, silenciosamente, em segundo plano. Mas para o usuário, não parece que o aplicativo foi fechado. Quando o usuário navega de volta para um aplicativo que o sistema operacional Android desligou, o Android reinicia esse aplicativo.
Nesta tarefa, você simula um desligamento de processo do Android e examina o que acontece com seu aplicativo quando ele é iniciado novamente.
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 adb
para fechar
o processo de seu aplicativo e ver o que acontece quando o Android desliga seu aplicativo.
adb
e pressione Return.Android Debug Bridge version X.XX.X
e terminam com
tags to be used by logcat (see logcat — help
), está tudo bem. Se, em vez disso, você vir
adb: command not found
, certifique-se de que o comando adb
esteja disponível em seu
caminho de execução. Para obter instruções, consulte "Adicionar adb ao caminho de execução" no capítulo Utilitários (em
inglês).adb shell am kill com.example.android.dessertclicker
Este comando diz a todos os dispositivos ou emuladores conectados para interromper o processo com o nome do
pacote dessertclicker
, mas apenas se o aplicativo estiver em segundo plano. Como seu aplicativo
estava em segundo plano, nada é mostrado na tela do dispositivo ou do emulador para indicar que o processo foi
interrompido. No Android Studio, clique na guia Run para ver uma mensagem que diz "Aplicativo
encerrado". Clique na guia Logcat para ver se o retorno de chamada onDestroy()
nunca foi executado — sua atividade simplesmente terminou.
onCreate()
. revenue
no aplicativo DessertClicker, o
sistema operacional Android não conhece esses dados ou sua importância para sua atividade. Você mesmo precisa
adicionar esses dados ao pacote.O método onSaveInstanceState()
é o retorno de chamada que você usa para salvar quaisquer dados de
que possa precisar se o sistema operacional Android destruir seu aplicativo. No diagrama de retorno de chamada
do ciclo de vida, onSaveInstanceState()
é chamado após a atividade ter sido interrompida. É chamado
sempre que seu aplicativo entra em segundo plano.
Pense na chamada onSaveInstanceState()
como uma medida de segurança; oferece a chance de salvar
uma pequena quantidade de informações em um pacote conforme sua atividade sai do primeiro plano. O sistema salva
esses dados agora, pois, se esperasse até desligar o aplicativo, o sistema operacional poderia estar sob pressão
de recursos. Salvando os dados todas as vezes garante que os dados atualizados no pacote estejam disponíveis
para restauração, se necessário.
MainActivity
, substitua o retorno de chamada onSaveInstanceState()
e adicione
uma instrução de registro Timber
. override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
Timber.i("onSaveInstanceState Called")
}
onSaveInstanceState()
ocorre logo após onPause()
e
onStop()
: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 dados do pacote de estado da instância.
onSaveInstanceState()
e observe o parâmetro outState
, que é do
tipo Bundle
.int
e boolean
, no
pacote. TransactionTooLargeException
.onSaveInstanceState()
, coloque o valor de revenue
(um inteiro) no pacote com o
método putInt()
:outState.putInt(KEY_REVENUE, revenue)
O método putInt()
(e métodos semelhantes da classe Bundle
como
putFloat()
e putString()
leva dois argumentos: Uma string para a chave (a constante
KEY_REVENUE
) e o valor real a ser salvo.
outState.putInt(KEY_DESSERT_SOLD, dessertsSold)
outState.putInt(KEY_TIMER_SECONDS, dessertTimer.secondsCount)
onCreate()
e examine a assinatura do método: override fun onCreate(savedInstanceState: Bundle) {
Observe que onCreate()
obtém um Bundle
cada vez que é chamado. Quando sua atividade é
reiniciada devido a um encerramento de processo, o pacote que você salvou é passado para
onCreate()
. Se sua atividade estava começando do zero, este pacote em onCreate()
é
null
. Portanto, se o pacote não for null
, você sabe que está "recriando" a atividade a
partir de um ponto previamente conhecido.
onCreate()
, após a configuração do DessertTimer
: if (savedInstanceState != null) {
revenue = savedInstanceState.getInt(KEY_REVENUE, 0)
}
O teste para null
determina se há dados no pacote ou se o pacote é null
, o que por
sua vez informa se o aplicativo foi iniciado do zero ou foi reiniciado -criado após um desligamento. Este teste
é um padrão comum para restaurar dados do pacote.
Observe que a chave que você usou aqui (KEY_REVENUE
) é a mesma chave que você usou para
putInt()
. Para ter certeza de usar a mesma chave todas as vezes, é uma prática recomendada definir
essas chaves como constantes. Você usa getInt()
para obter os dados do pacote, assim como usou
putInt()
para colocar os dados no pacote. O método getInt()
leva dois argumentos:
"key_revenue"
para o valor da receita.O número inteiro que você obtém do pacote é então atribuído à variável revenue
e a IU usará esse
valor.
getInt()
para restaurar o número de sobremesas vendidas e o valor do
cronômetro: if (savedInstanceState != null) {
revenue = savedInstanceState.getInt(KEY_REVENUE, 0)
dessertsSold = savedInstanceState.getInt(KEY_DESSERT_SOLD, 0)
dessertTimer.secondsCount =
savedInstanceState.getInt(KEY_TIMER_SECONDS, 0)
}
adb
para desligar o processo do
aplicativo. adb shell am kill com.example.android.dessertclicker
MainActivity
, examine o método showCurrentDessert()
. Observe que este método
determina qual imagem de sobremesa deve ser exibida na atividade com base no número atual de sobremesas
vendidas e na lista de sobremesas na variável allDesserts
. for (dessert in allDesserts) {
if (dessertsSold >= dessert.startProductionAmount) {
newDessert = dessert
}
else break
}
Este método depende do número de sobremesas vendidas para escolher a imagem certa. Portanto, você não precisa
fazer nada para armazenar uma referência à imagem no pacote em onSaveInstanceState()
. Nesse pacote,
você já está armazenando a quantidade de sobremesas vendidas.
onCreate()
, no bloco que restaura o estado do pacote, chame
showCurrentDessert()
: 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()
}
adb
para encerrar o processo.
Use a tela Recentes para retornar ao aplicativo. Observe agora que os valores das sobremesas contadas, receita
total e imagem da sobremesa foram restaurados corretamente.Há um último caso especial no gerenciamento do ciclo de vida de atividade e fragmento que é importante entender: Como as mudanças na configuração afetam o ciclo de vida de suas atividades e fragmentos.
Uma mudança de configuração ocorre quando o estado do dispositivo muda tão radicalmente que a maneira mais fácil para o sistema resolver a mudança é encerrar e reconstruir a atividade. Por exemplo, se o usuário alterar a linguagem do dispositivo, todo o layout pode precisar ser alterado para acomodar diferentes direções de texto. Se o usuário conectar o dispositivo a um dock ou adicionar um teclado físico, o layout do aplicativo pode precisar tirar proveito de um tamanho de tela ou layout diferente. E se a orientação do dispositivo mudar - se o dispositivo for girado de retrato para paisagem ou vice-versa - o layout pode precisar ser alterado para se ajustar à nova orientação.
Control
e as teclas de seta (Command
e as teclas
de seta em um Mac).MainActivity
. MainActivity
, comente todo o método onSaveInstanceState()
. onSaveInstanceState()
para colocar os dados do seu
aplicativo no pacote. Em seguida, restaure os dados em onCreate()
, para evitar a perda de dados
do estado de atividade se o dispositivo for girado.MainActivity
, descomente o método onSaveInstanceState()
, execute o aplicativo,
clique no bolinho e gire o aplicativo ou dispositivo. Observe que, desta vez, os dados da sobremesa são
retidos durante a rotação de atividades. Projeto Android Studio: DessertClickerFinal
onStart()
, precisará
pausar ou parar o cronômetro em onStop()
. onCreate()
apenas para inicializar as partes do seu aplicativo que são executadas uma vez,
quando o aplicativo é iniciado pela primeira vez. Use onStart()
para iniciar as partes do seu
aplicativo que são executadas quando o aplicativo é iniciado e sempre que o aplicativo retorna ao primeiro
plano. Activity
e Fragment
. Os proprietários do ciclo de vida implementam a
interface LifecycleOwner
.LifecycleObserver
.Lifecycle
contêm os estados reais do ciclo de vida e acionam eventos quando o ciclo
de vida muda. Para criar uma classe com reconhecimento de ciclo de vida:
LifecycleObserver
em classes que precisam estar cientes do ciclo de
vida.@OnLifecycleEvent(Lifecycle.Event.ON_START)
indica que o método está observando o evento de ciclo
de vida onStart
.adb
) é uma ferramenta de linha de comando que permite enviar instruções
para emuladores e dispositivos conectados ao seu computador. Você pode usar adb
para simular o
desligamento do processo em seu aplicativo.onDestroy()
não é chamado. O aplicativo simplesmente para.onStop()
ser chamado, os dados do
aplicativo são salvos em um pacote. Alguns dados do aplicativo, como o conteúdo de um EditText
,
são salvos automaticamente.Bundle
, que é uma coleção de chaves e valores. As chaves são sempre
strings. onSaveInstanceState()
para salvar outros dados no pacote que você
deseja reter, mesmo se o aplicativo foi encerrado automaticamente. Para colocar dados no pacote, use os
métodos do pacote que começam com put
, como putInt()
. onRestoreInstanceState()
, ou mais comumente
em onCreate()
. O método onCreate()
tem um parâmetro savedInstanceState
que contém o pacote.savedInstanceState
contém null
, a atividade foi iniciada sem um
pacote de estado e não há dados de estado para recuperar.Bundle
que começam com
get
, como getInt()
.onCreate()
. onSaveInstanceState()
.
Documentação para desenvolvimento em Android:
Activity
(referência de API)LifecycleOwner
Lifecycle
LifecycleObserver
onSaveInstanceState()
De outros:
Esta seção lista as possíveis tarefas de casa para os alunos que estão trabalhando neste tutorial como parte de um curso ministrado por um instrutor.
Abra o aplicativo DiceRoller na Lição 1. (Você pode baixar o aplicativo aqui se não o tiver). Compile e execute o aplicativo e observe que, se
você girar o dispositivo, o valor atual de os dados estão perdidos. Implemente
onSaveInstanceState()
para reter esse valor no pacote e restaure esse valor em
onCreate()
.
Seu aplicativo contém uma simulação de física que requer computação pesada para ser exibida. Em seguida, o usuário recebe uma chamada telefônica. Qual dos seguintes é verdadeiro?
Qual método de ciclo de vida você deve substituir para pausar a simulação quando o aplicativo não está na tela?
onDestroy()
onStop()
onPause()
onSaveInstanceState()
Para tornar uma classe ciente do ciclo de vida por meio da biblioteca de ciclo de vida do Android, qual interface a classe deve implementar?
Lifecycle
LifecycleOwner
Lifecycle.Event
LifecycleObserver
Sob quais circunstâncias o método onCreate()
em sua atividade recebe um Bundle
com
dados (ou seja, o Bundle
não é null
)? Mais de uma resposta pode ser aplicável.
Comece a próxima lição:
Para obter enlaces para outros tutoriais neste curso, consulte a página de destino dos tutoriais Fundamentos de Android em Kotlin.