A Visualização do desenvolvedor para Android 11 já está disponível. Teste e compartilhe seu feedback.

Como gerenciar ciclos de vida com componentes com reconhecimento de ciclo de vida   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, normalmente, 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 aqueles 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 framework do Android tem ciclos de vida anexados a eles. Os ciclos de vida são gerenciados pelo sistema operacional ou pelo código do framework 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 essa amostra pareça boa, em um app real, você acabaria tendo 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 nos métodos do ciclo de vida, como onStart() e onStop(), o que dificulta a manutenção.

Além disso, não há qualquer garantia de que o componente será iniciado antes da atividade ou que o fragmento será interrompido. Isso é especialmente verdadeiro se precisarmos executar uma operação longa, como algumas verificações 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 do 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 você a lidar com esses problemas de maneira resiliente e isolada.

Lifecycle

O 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
Os 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.
Diagrama dos estados do ciclo de vida
Figura 1. Estados e eventos que compõem o ciclo de vida da atividade do Android.

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 acrescentando anotações aos próprios métodos. Depois, é possível 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 : LifecycleObserver {

        @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
        fun connectListener() {
            ...
        }

        @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
        fun disconnectListener() {
            ...
        }
    }

    myLifecycleOwner.getLifecycle().addObserver(MyObserver())
    

Java

    public class MyObserver implements LifecycleObserver {
        @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
        public void connectListener() {
            ...
        }

        @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
        public void disconnectListener() {
            ...
        }
    }

    myLifecycleOwner.getLifecycle().addObserver(new MyObserver());
    

No exemplo acima, o objeto myLifecycleOwner implementa a interface LifecycleOwner, que é explicada na seção a seguir.

LifecycleOwner

O LifecycleOwner é uma interface de método único que indica que a classe tem um Lifecycle. Ele tem um único método, o getLifecycle(), que precisa ser implementado pela classe. Se você quiser 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 escrever componentes que funcionam com elas. Qualquer classe de aplicativo personalizada pode implementar a interface LifecycleOwner.

Os componentes que implementam o LifecycleObserver funcionam perfeitamente com aqueles que implementam o LifecycleOwner, porque um proprietário pode fornecer um ciclo de vida, que um observador pode se registrar para assistir.

Para o exemplo de rastreamento de local, podemos fazer a classe MyLocationListener implementar LifecycleObserver 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
    ) {

        private var enabled = false

        @OnLifecycleEvent(Lifecycle.Event.ON_START)
        fun start() {
            if (enabled) {
                // connect
            }
        }

        fun enable() {
            enabled = true
            if (lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) {
                // connect if not connected
            }
        }

        @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
        fun stop() {
            // disconnect if connected
        }
    }
    

Java

    class MyLocationListener implements LifecycleObserver {
        private boolean enabled = false;
        public MyLocationListener(Context context, Lifecycle lifecycle, Callback callback) {
           ...
        }

        @OnLifecycleEvent(Lifecycle.Event.ON_START)
        void start() {
            if (enabled) {
               // connect
            }
        }

        public void enable() {
            enabled = true;
            if (lifecycle.getCurrentState().isAtLeast(STARTED)) {
                // connect if not connected
            }
        }

        @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
        void stop() {
            // disconnect if connected
        }
    }
    

Com essa implementação, nossa classe LocationListener tem reconhecimento total do ciclo de vida. Se for necessário usar nosso LocationListener de outra atividade ou fragmento, precisaremos apenas 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 posteriores já implementam a interface LifecycleOwner.

Se você tem uma classe personalizada que quer transformar em um LifecycleOwner, é possível utilizar a classe LifecycleRegistry, mas será preciso 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 devem tentar adquirir os próprios dados. Em vez disso, use um ViewModel para fazer isso e observe um objeto LiveData para refletir as mudanças de volta para as visualizações.
  • Tente escrever IUs baseadas em dados em que a responsabilidade do controlador de IU é 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. O ViewModel precisa atuar como conector entre seu controlador de IU e o restante do app. Observe, entretanto, que não é responsabilidade do ViewModel buscar dados (por exemplo, de uma rede). Em vez disso, o ViewModel precisa chamar o componente apropriado para buscar os dados e, em seguida, fornecer o resultado de volta para o controlador de IU.
  • Use a Data Binding para manter uma interface simples entre suas visualizações e o controlador de IU. Isso permite tornar suas visualizações mais declarativas e minimizar o código de atualização que você precisa escrever nas suas atividades e seus fragmentos. Se você preferir fazer isso na linguagem de programação Java, use uma biblioteca como a Butter Knife (link em inglês) para evitar códigos clichê e conseguir uma melhor abstração.
  • Se sua IU for complexa, considere criar uma classe presenter (link em inglês) para lidar com as modificações de IU. Essa pode ser uma tarefa trabalhosa, mas que pode tornar seus componentes de IU mais fáceis de serem testados.
  • Evite referenciar um contexto de View ou Activity no seu ViewModel. Se o ViewModel 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 alterne para atualizações aproximadas quando o app estiver em segundo plano. O LiveData, um componente com reconhecimento de ciclo de vida, permite que seu app atualize automaticamente a IU quando a utilização 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 o app voltar para o primeiro plano.

Gerenciar eventos "on stop"

Quando um Lifecycle pertence a um AppCompatActivity ou Fragment, o estado do Lifecycle muda para CREATED, e o evento ON_STOP é despachado quando o onSaveInstanceState() do Fragment ou AppCompatActivity é chamado.

Quando um estado de Fragment ou AppCompatActivity é salvo por meio de onSaveInstanceState(), a IU dele é 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, e é por isso que o FragmentManager gerará uma exceção se o app executar um FragmentTransaction depois que o estado tiver sido salvo. Consulte commit() para saber mais detalhes.

LiveData impede que esse caso extremo ocorra, evitando chamar o observador se o Lifecycle associado ao observador não for pelo menos STARTED. Internamente, ele chama isAtLeast() antes de decidir invocar o observador.

Infelizmente, o método onStop() de AppCompatActivity é chamado após onSaveInstanceState(), o que deixa 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 Lifecycle na versão beta2 e anteriores marca o estado como CREATED sem despachar o evento, para que qualquer código que verifique o estado atual receba o valor real, mesmo que o evento não seja despachado até que onStop() seja chamado pelo sistema.

Essa solução tem dois grandes problemas:

  • Na API de nível 23 ou anterior, o sistema Android realmente salva o estado de uma atividade, mesmo que ele seja parcialmente coberto por outra atividade. Em outras palavras, o sistema Android chama onSaveInstanceState(), mas não necessariamente onStop(). 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 à classe LiveData precisa implementar a solução alternativa fornecida pelo Lifecycle versão beta 2 e anteriores.

Outros recursos

Para saber mais sobre como lidar com ciclos de vida com componentes que reconhecem o ciclo de vida, consulte os seguintes recursos.

Amostras (links em inglês)

Codelabs (link em inglês)

Blogs (link em inglês)