Como gerenciar ciclos de vida com componentes que os reconhecem Parte do Android Jetpack.
Os componentes com reconhecimento de ciclo de vida executam ações em resposta a uma mudança no status do ciclo de vida de outro componente, como atividades e fragmentos. Esses componentes ajudam você a produzir códigos mais organizados e, com frequência, mais leves e mais fáceis de manter.
Um padrão comum é implementar as ações dos componentes dependentes nos métodos de ciclo de vida de atividades e fragmentos. No entanto, esse padrão leva a um código desorganizado e à proliferação de erros. Usando componentes com reconhecimento de ciclo de vida, é possível mover o código de componentes dependentes dos métodos de ciclo de vida para os próprios componentes.
O pacote androidx.lifecycle
fornece classes e interfaces que permitem criar componentes com reconhecimento de ciclo de vida,
que são componentes que podem ajustar automaticamente o próprio
comportamento com base no estado atual do ciclo de vida de uma atividade ou um fragmento.
A maioria dos componentes de app definidos no Android Framework tem ciclos de vida anexados a eles. Os ciclos de vida são gerenciados pelo sistema operacional ou pelo código da estrutura em execução no processo. Eles são fundamentais para o modo como o Android funciona e seu aplicativo precisa respeitá-los. Não fazer isso pode provocar vazamentos de memória ou até mesmo falhas no aplicativo.
Imagine que temos uma atividade que mostra o local do dispositivo na tela. Uma implementação comum pode ser semelhante a esta:
Kotlin
internal class MyLocationListener( private val context: Context, private val callback: (Location) -> Unit ) { fun start() { // connect to system location service } fun stop() { // disconnect from system location service } } class MyActivity : AppCompatActivity() { private lateinit var myLocationListener: MyLocationListener override fun onCreate(...) { myLocationListener = MyLocationListener(this) { location -> // update UI } } public override fun onStart() { super.onStart() myLocationListener.start() // manage other components that need to respond // to the activity lifecycle } public override fun onStop() { super.onStop() myLocationListener.stop() // manage other components that need to respond // to the activity lifecycle } }
Java
class MyLocationListener { public MyLocationListener(Context context, Callback callback) { // ... } void start() { // connect to system location service } void stop() { // disconnect from system location service } } class MyActivity extends AppCompatActivity { private MyLocationListener myLocationListener; @Override public void onCreate(...) { myLocationListener = new MyLocationListener(this, (location) -> { // update UI }); } @Override public void onStart() { super.onStart(); myLocationListener.start(); // manage other components that need to respond // to the activity lifecycle } @Override public void onStop() { super.onStop(); myLocationListener.stop(); // manage other components that need to respond // to the activity lifecycle } }
Mesmo que esse exemplo pareça bom, em um app real você teria muitas
chamadas que gerenciam a IU e outros componentes em resposta ao estado atual
do ciclo de vida. O gerenciamento de vários componentes coloca uma quantidade considerável de
código em métodos de ciclo de vida, como onStart()
e
onStop()
, o que dificulta a manutenção.
Além disso, não há garantia de que o componente será iniciado antes da atividade ou de
que o fragmento será interrompido. Isso é verdadeiro especialmente se for necessário executar uma
operação de longa duração, como alguma verificação de configuração em onStart()
. Isso pode causar uma disputa em que o método onStop()
termina antes do onStart()
, mantendo o componente ativo por mais tempo que o
necessário.
Kotlin
class MyActivity : AppCompatActivity() { private lateinit var myLocationListener: MyLocationListener override fun onCreate(...) { myLocationListener = MyLocationListener(this) { location -> // update UI } } public override fun onStart() { super.onStart() Util.checkUserStatus { result -> // what if this callback is invoked AFTER activity is stopped? if (result) { myLocationListener.start() } } } public override fun onStop() { super.onStop() myLocationListener.stop() } }
Java
class MyActivity extends AppCompatActivity { private MyLocationListener myLocationListener; public void onCreate(...) { myLocationListener = new MyLocationListener(this, location -> { // update UI }); } @Override public void onStart() { super.onStart(); Util.checkUserStatus(result -> { // what if this callback is invoked AFTER activity is stopped? if (result) { myLocationListener.start(); } }); } @Override public void onStop() { super.onStop(); myLocationListener.stop(); } }
O pacote androidx.lifecycle
fornece classes e interfaces que ajudam a lidar com esses problemas
de forma resiliente e isolada.
Lifecycle
Lifecycle
é uma classe
que contém as informações sobre o estado do ciclo de vida de um componente (como uma
atividade ou um fragmento) e permite que outros objetos observem esse estado.
O Lifecycle
usa duas enumerações
principais para rastrear o status do ciclo de vida do componente associado a ele:
- Evento
- Eventos de ciclo de vida que são despachados do framework e da classe
Lifecycle
. Esses eventos são mapeados para os eventos de callback em atividades e fragmentos. - Estado
- O estado atual do componente rastreado pelo objeto
Lifecycle
.
Pense nos estados como nós de um gráfico e nos eventos como as arestas entre esses nós.
Uma classe pode monitorar o status do ciclo de vida do componente implementando a
DefaultLifecycleObserver
e substituindo métodos correspondentes, como onCreate
, onStart
etc.
Depois, você pode adicionar um observador chamando o método
addObserver()
da classe Lifecycle
e transmitindo uma instância do seu observador, conforme mostrado no exemplo
a seguir:
Kotlin
class MyObserver : DefaultLifecycleObserver { override fun onResume(owner: LifecycleOwner) { connect() } override fun onPause(owner: LifecycleOwner) { disconnect() } } myLifecycleOwner.getLifecycle().addObserver(MyObserver())
Java
public class MyObserver implements DefaultLifecycleObserver { @Override public void onResume(LifecycleOwner owner) { connect() } @Override public void onPause(LifecycleOwner owner) { disconnect() } } myLifecycleOwner.getLifecycle().addObserver(new MyObserver());
No exemplo acima, o objeto myLifecycleOwner
implementa a interface
LifecycleOwner
,
que é explicada na seção a seguir.
LifecycleOwner
LifecycleOwner
é uma
única interface de método que indica que a classe tem um
Lifecycle
. Ela tem um
único método,
getLifecycle()
,
que precisa ser implementado pela classe.
Caso esteja tentando gerenciar o ciclo de vida de todo um processo de aplicativo, consulte
ProcessLifecycleOwner
.
Essa interface abstrai a propriedade de um
Lifecycle
de classes individuais,
como Fragment
e AppCompatActivity
, e permite gravar componentes que
funcionem com elas. Qualquer classe de aplicativo personalizada pode implementar a
interface
LifecycleOwner
.
Os componentes que implementam
DefaultLifecycleObserver
funcionam perfeitamente com aqueles que implementam
LifecycleOwner
,
porque um proprietário pode fornecer um ciclo de vida, que um observador pode se registrar
para monitorar.
Para o exemplo de rastreamento de local, é possível fazer a classe MyLocationListener
implementar DefaultLifecycleObserver
e, em seguida, inicializá-la com o
Lifecycle
da atividade no método onCreate()
. Isso permite que a classe
MyLocationListener
seja autossuficiente, o que significa que a lógica para
reagir a mudanças no status do ciclo de vida é declarada em MyLocationListener
, em vez
da atividade. Armazenar os componentes individuais na sua própria lógica torna
a lógica de atividades e fragmentos mais fácil de gerenciar.
Kotlin
class MyActivity : AppCompatActivity() { private lateinit var myLocationListener: MyLocationListener override fun onCreate(...) { myLocationListener = MyLocationListener(this, lifecycle) { location -> // update UI } Util.checkUserStatus { result -> if (result) { myLocationListener.enable() } } } }
Java
class MyActivity extends AppCompatActivity { private MyLocationListener myLocationListener; public void onCreate(...) { myLocationListener = new MyLocationListener(this, getLifecycle(), location -> { // update UI }); Util.checkUserStatus(result -> { if (result) { myLocationListener.enable(); } }); } }
Um caso de uso comum é evitar invocar certos callbacks se o
Lifecycle
não estiver em bom
estado no momento. Por exemplo, se o callback executar uma transação de fragmento depois
que o estado da atividade for salvo, ele causará uma falha, então nunca é recomendável
invocar esse callback.
Para facilitar esse caso de uso, a classe
Lifecycle
permite
que outros objetos consultem o estado atual.
Kotlin
internal class MyLocationListener( private val context: Context, private val lifecycle: Lifecycle, private val callback: (Location) -> Unit ): DefaultLifecycleObserver { private var enabled = false override fun onStart(owner: LifecycleOwner) { if (enabled) { // connect } } fun enable() { enabled = true if (lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) { // connect if not connected } } override fun onStop(owner: LifecycleOwner) { // disconnect if connected } }
Java
class MyLocationListener implements DefaultLifecycleObserver { private boolean enabled = false; public MyLocationListener(Context context, Lifecycle lifecycle, Callback callback) { ... } @Override public void onStart(LifecycleOwner owner) { if (enabled) { // connect } } public void enable() { enabled = true; if (lifecycle.getCurrentState().isAtLeast(STARTED)) { // connect if not connected } } @Override public void onStop(LifecycleOwner owner) { // disconnect if connected } }
Com essa implementação, a classe LocationListener
tem reconhecimento
total do ciclo de vida. Se for necessário usar o LocationListener
de outra atividade
ou fragmento, basta inicializá-lo. Todas as operações de configuração e desmontagem
são gerenciadas pela própria classe.
Se uma biblioteca fornece classes que precisam trabalhar com o ciclo de vida do Android, recomendamos que você use componentes com reconhecimento de ciclo de vida. Seus clientes de biblioteca podem integrar facilmente esses componentes sem o gerenciamento manual do ciclo de vida no lado do cliente.
Implementar um LifecycleOwner personalizado
Fragmentos e atividades na Biblioteca de Suporte 26.1.0 e versões mais recentes já implementam
a interface
LifecycleOwner
.
Caso você tenha uma classe personalizada que queira transformar em um
LifecycleOwner
é possível utilizar a classe
LifecycleRegistry.
Contudo, será necessário encaminhar eventos para essa classe, conforme mostrado no
exemplo de código a seguir:
Kotlin
class MyActivity : Activity(), LifecycleOwner { private lateinit var lifecycleRegistry: LifecycleRegistry override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) lifecycleRegistry = LifecycleRegistry(this) lifecycleRegistry.markState(Lifecycle.State.CREATED) } public override fun onStart() { super.onStart() lifecycleRegistry.markState(Lifecycle.State.STARTED) } override fun getLifecycle(): Lifecycle { return lifecycleRegistry } }
Java
public class MyActivity extends Activity implements LifecycleOwner { private LifecycleRegistry lifecycleRegistry; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); lifecycleRegistry = new LifecycleRegistry(this); lifecycleRegistry.markState(Lifecycle.State.CREATED); } @Override public void onStart() { super.onStart(); lifecycleRegistry.markState(Lifecycle.State.STARTED); } @NonNull @Override public Lifecycle getLifecycle() { return lifecycleRegistry; } }
Práticas recomendadas para componentes com reconhecimento de ciclo de vida
- Mantenha seus controladores de IU (atividades e fragmentos) o mais simples possível. Eles
não podem tentar adquirir os próprios dados. Em vez disso, use um
ViewModel
para fazer isso e observe um objetoLiveData
para refletir as mudanças nas visualizações. - Tente escrever IUs baseadas em dados em que a responsabilidade do controlador é
atualizar as visualizações à medida que os dados são modificados ou notificar as ações do usuário de volta para o
ViewModel
. - Inclua sua lógica de dados na classe
ViewModel
.ViewModel
servirá como o conector entre o controlador de IU e o restante do app. Entretanto, tenha cuidado, porque não é responsabilidade doViewModel
buscar dados (por exemplo, de uma rede). Em vez disso,ViewModel
precisa chamar o componente adequado para buscar os dados e, em seguida, fornecer o resultado para o controlador de IU. - Use Data Binding para manter uma interface simples entre suas visualizações e o controlador de IU. Isso permite tornar suas exibições mais declarativas e minimizar o código de atualização que você precisa escrever nas suas atividades e fragmentos. Caso você prefira fazer isso na linguagem de programação Java, use uma biblioteca como a Butter Knife para evitar códigos boilerplate e conseguir uma melhor abstração.
- Se sua IU for complexa, considere criar uma classe presenter para lidar com as modificações de IU. Essa pode ser uma tarefa trabalhosa, mas que pode tornar seus componentes da IU mais fáceis de serem testados.
- Evite referenciar um contexto
View
ouActivity
noViewModel
. Se oViewModel
durar mais que a atividade (no caso de mudanças de configuração), sua atividade vazará e não será devidamente descartada pelo coletor de lixo. - Use corrotinas de Kotlin para gerenciar tarefas de longa duração e outras operações que podem ser executadas de forma assíncrona.
Casos de uso para componentes com reconhecimento de ciclo de vida
Componentes com reconhecimento de ciclo de vida podem facilitar muito o gerenciamento de ciclos de vida em diversos casos. Vejas alguns exemplos:
- Alternar entre atualizações de localização aproximada ou detalhada. Use
componentes com reconhecimento de ciclo de vida para ativar atualizações de localização detalhadas enquanto seu
app de local estiver visível e mude para atualizações aproximadas quando o app estiver
em segundo plano. O
LiveData
, um componente com reconhecimento de ciclo de vida, permite que o app atualize a IU automaticamente quando o usuário muda de local. - Parar e iniciar o armazenamento de vídeos em buffer. Use componentes com reconhecimento de ciclo de vida para iniciar o armazenamento de vídeos em buffer o mais rápido possível, mas adiar a reprodução até que o app seja totalmente iniciado. Você também pode usar componentes com reconhecimento de ciclo de vida para encerrar o armazenamento em buffer quando o app for destruído.
- Iniciar e interromper a conectividade de rede. Use componentes com reconhecimento de ciclo de vida para ativar a atualização em tempo real (streaming) de dados de rede enquanto um app estiver em primeiro plano e também para pausá-la automaticamente quando ele estiver em segundo plano.
- Pausar e retomar drawables animados. Use componentes com reconhecimento de ciclo de vida para pausar drawables animados quando o app estiver em segundo plano e retomar os drawables depois que ele voltar para o primeiro plano.
Gerenciar eventos "on stop"
Quando um Lifecycle
pertence a uma AppCompatActivity
ou um Fragment
, o estado do Lifecycle
muda para CREATED
e
o evento ON_STOP
é enviado quando o AppCompatActivity
ou
o onSaveInstanceState()
do Fragment
é chamado.
Quando o estado de um Fragment
ou uma AppCompatActivity
é salvo pelo
onSaveInstanceState()
, a IU
é considerada imutável até que
ON_START
seja chamado. Tentar modificar a IU depois que o estado foi salvo provavelmente causará
inconsistências no estado de navegação do aplicativo. É por esse motivo que o FragmentManager
gerará uma exceção se o app executar uma
FragmentTransaction
depois que o estado tiver sido salvo. Consulte
commit()
para mais detalhes.
LiveData
impede que esse caso extremo ocorra, evitando
chamar o observador se o Lifecycle
associado ao observador não estiver pelo menos
STARTED
.
Internamente, ele chama
isAtLeast()
antes de decidir invocar o observador.
Infelizmente, o método onStop()
da AppCompatActivity
é chamado depois de
onSaveInstanceState()
.
Isso gera uma lacuna em que as mudanças de estado da IU não são permitidas, mas o
Lifecycle
ainda não foi passado para o
estado
CREATED
.
Para evitar esse problema, a classe do Lifecycle
na versão beta2
e anteriores marca o estado como
CREATED
sem despachar o evento para que qualquer código que verificar o estado atual
receba o valor real, mesmo que o evento não seja despachado até onStop()
ser chamado pelo sistema.
Essa solução tem dois grandes problemas:
- Na API de nível 23 e anteriores, o sistema Android salva o estado de uma
atividade, mesmo que ele esteja parcialmente coberto por outra atividade. Em outras
palavras, o sistema Android chama
onSaveInstanceState()
, mas não necessariamenteonStop()
. Isso cria um intervalo potencialmente longo em que o observador ainda pensa que o ciclo de vida está ativo, mesmo que o estado da IU não possa ser modificado. - Qualquer classe que queira expor um comportamento semelhante ao da classe
LiveData
precisa implementar a solução alternativa fornecida pela versãobeta 2
e anteriores doLifecycle
.
Outros recursos
Para saber mais sobre como lidar com ciclos de vida com componentes que os reconhecem, consulte os seguintes recursos.
Amostras
- Sunflower (link em inglês), um app que demonstra as práticas recomendadas com os Componentes de arquitetura
Codelabs
Blogs
Recomendados para você
- Observação: o texto do link aparece quando o JavaScript está desativado.
- Visão geral do LiveData
- Usar corrotinas do Kotlin com componentes que reconhecem o ciclo de vida
- Módulo Saved State para ViewModel