Ciclo de vida no Android — Um estudo de caso
A intenção desse post é compartilhar um estudo de caso de ciclos de vida no Android que vai um pouco além do "hello world". Se você ainda está aprendendo sobre ciclos de vida recomendo ler a documentação oficial do Google:
ou ler a belíssima sequência de posts escritos pelo Jose Alcérreca (The Android Lifecycle cheat sheet — part I, II, III and IV):
Quando estamos aprendendo Android sempre nos deparamos com exemplos bem básicos de ciclo de vida, e eu também achava que entender o básico era suficiente para todos os casos… até eu me deparar com a seguinte situação em uma aplicação:
Nesse exemplo nós temos a seguinte sequência de eventos:
- #1 Usuário entra na app por uma activity que contém um fragment
- #2 Usuário navega para segunda activity que também contém um fragment
- #3 Usuário navega para terceira activity que por sua vez é transparente
- #4 Usuário muda a orientação da tela na activity transparente
- #5 Com a tela em landscape o usuário aperta back e volta para a segunda activity
- #6 Com a tela ainda em landscape o usuário aperta back e volta para a primeira activity
- #7 Por último, o usuário aperta back e sai da app
Nesse momento parei para pensar… O que acontece com o ciclo de vida de cada activity e fragment durante essa sequencia de eventos? 🤔🤔🤔 Mais especificamente:
- O que acontece com a primeira activity, que está bem abaixo na stack, quando a gente muda a orientação da tela na terceira activity?
- O que acontece com a segunda activity quando acontece uma rotação com algo transparente sobre ela?
- O que acontece com os fragments no meio dessa confusão toda?
Um pouco difícil de visualizar e responder, pelo menos para mim... Então eu criei esse repositório para analisar o caso:
Abaixo estão os resultados do estudo de caso. Para implementar esse fluxo eu utilizei o SDK 28 sem alterar o launch mode de nenhuma activity.
O que acontece com as activities durante o fluxo?
Entra na app
🔴Primeira Activity onCreate(null)
🔴Primeira Activity onStart
🔴Primeira Activity onResume
Navega para segunda activity
🔴Primeira Activity onPause
🔵Segunda Activity onCreate(null)
🔵Segunda Activity onStart
🔵Segunda Activity onResume
🔴Primeira Activity onStop
Navega para activity transparente
🔵Segunda Activity onPause
⚪️Terceira [transparente] Activity onCreate(null)
⚪️Terceira [transparente] Activity onStart
⚪️Terceira [transparente] Activity onResume
Muda orientação da tela
⚪️Terceira [transparente] Activity onPause
⚪️Terceira [transparente] Activity onStop
⚪️Terceira [transparente] Activity onDestroy
⚪️Terceira [transparente] Activity onCreate(bundle)
⚪️Terceira [transparente] Activity onStart
⚪️Terceira [transparente] Activity onRestoreInstanceState(bundle)
⚪️Terceira [transparente] Activity onResume
🔵Segunda Activity onStop
🔵Segunda Activity onDestroy
🔵Segunda Activity onCreate(bundle)
🔵Segunda Activity onStart
🔵Segunda Activity onRestoreInstanceState(bundle)
🔵Segunda Activity onResume
🔵Segunda Activity onPause
Pressiona back e sai da activity transparente
⚪️Terceira [transparente] Activity onPause
🔵Segunda Activity onResume
⚪️Terceira [transparente] Activity onStop
⚪️Terceira [transparente] Activity onDestroy
Pressiona back e sai da segunda activity
🔵Segunda Activity onPause
🔴Primeira Activity onDestroy
🔴Primeira Activity onCreate(bundle)
🔴Primeira Activity onStart
🔴Primeira Activity onRestoreInstanceState(bundle)
🔴Primeira Activity onResume
🔵Segunda Activity onStop
🔵Segunda Activity onDestroy
Pressiona back e sai da app
🔴Primeira Activity onPause
🔴Primeira Activity onStop
🔴Primeira Activity onDestroy
O que acontece com os fragments durante o fluxo?
Entra na app
🔶Primeiro Fragment onAttach
🔶Primeiro Fragment onCreate
🔶Primeiro Fragment onCreateView
🔶Primeiro Fragment onActivityCreated
🔶Primeiro Fragment onStart
🔶Primeiro Fragment onResume
Navega para segunda activity
🔶Primeiro Fragment onPause
🔷Segundo Fragment onAttach
🔷Segundo Fragment onCreate
🔷Segundo Fragment onCreateView
🔷Segundo Fragment onActivityCreated
🔷Segundo Fragment onStart
🔷Segundo Fragment onResume
🔶Primeiro Fragment onStop
Navega para activity transparente
🔷Segundo Fragment onPause
Muda orientação da tela
🔷Segundo Fragment onStop
🔷Segundo Fragment onDestroyView
🔷Segundo Fragment onDestroy
🔷Segundo Fragment onDetach
🔷Segundo Fragment onAttach
🔷Segundo Fragment onCreate
🔷Segundo Fragment onCreateView
🔷Segundo Fragment onActivityCreated
🔷Segundo Fragment onStart
🔷Segundo Fragment onResume
🔷Segundo Fragment onPause
Pressiona back e sai da activity transparente
🔷Segundo Fragment onResume
Pressiona back e sai da segunda activity
🔷Segundo Fragment onPause
🔶Primeiro Fragment onDestroyView
🔶Primeiro Fragment onDestroy
🔶Primeiro Fragment onDetach
🔶Primeiro Fragment onAttach
🔶Primeiro Fragment onCreate
🔶Primeiro Fragment onCreateView
🔶Primeiro Fragment onActivityCreated
🔶Primeiro Fragment onStart
🔶Primeiro Fragment onResume
🔷Segundo Fragment onStop
🔷Segundo Fragment onDestroyView
🔷Segundo Fragment onDestroy
🔷Segundo Fragment onDetach
Pressiona back e sai da app
🔶Primeiro Fragment onPause
🔶Primeiro Fragment onStop
🔶Primeiro Fragment onDestroyView
🔶Primeiro Fragment onDestroy
🔶Primeiro Fragment onDetach
O que acontece com as activities e fragments durante o fluxo?
Entra na app
🔶Primeiro Fragment onAttach
🔶Primeiro Fragment onCreate
🔶Primeiro Fragment onCreateView
🔴Primeira Activity onCreate(null)
🔶Primeiro Fragment onActivityCreated
🔶Primeiro Fragment onStart
🔴Primeira Activity onStart
🔴Primeira Activity onResume
🔶Primeiro Fragment onResume
Navega para segunda activity
🔶Primeiro Fragment onPause
🔴Primeira Activity onPause
🔷Segundo Fragment onAttach
🔷Segundo Fragment onCreate
🔷Segundo Fragment onCreateView
🔵Segunda Activity onCreate(null)
🔷Segundo Fragment onActivityCreated
🔷Segundo Fragment onStart
🔵Segunda Activity onStart
🔵Segunda Activity onResume
🔷Segundo Fragment onResume
🔶Primeiro Fragment onStop
🔴Primeira Activity onStop
Navega para activity transparente
🔷Segundo Fragment onPause
🔵Segunda Activity onPause
⚪️️️Terceira [transparente] Activity onCreate(null)
⚪️Terceira [transparente] Activity onStart
⚪️Terceira [transparente] Activity onResume
Muda orientação da tela
⚪️Terceira [transparente] Activity onPause
⚪️Terceira [transparente] Activity onStop
⚪️Terceira [transparente] Activity onDestroy
⚪️Terceira [transparente] Activity onCreate(bundle)
⚪️Terceira [transparente] Activity onStart
⚪️Terceira [transparente] Activity onRestoreInstanceState(bundle)
⚪️Terceira [transparente] Activity onResume
🔷Segundo Fragment onStop
🔵Segunda Activity onStop
🔷Segundo Fragment onDestroyView
🔷Segundo Fragment onDestroy
🔷Segundo Fragment onDetach
🔵Segunda Activity onDestroy
🔷Segundo Fragment onAttach
🔷Segundo Fragment onCreate
🔷Segundo Fragment onCreateView
🔵Segunda Activity onCreate(bundle)
🔷Segundo Fragment onActivityCreated
🔷Segundo Fragment onStart
🔵Segunda Activity onStart
🔷Segundo Activity onRestoreInstanceState(bundle)
🔵Segunda Activity onResume
🔷Segundo Fragment onResume
🔷Segundo Fragment onPause
🔵Segunda Activity onPause
Pressiona back e sai da activity transparente
⚪️Terceira [transparente] Activity onPause
🔵Segunda Activity onResume
🔷Segundo Fragment onResume
⚪️Terceira [transparente] Activity onStop
⚪️Terceira [transparente] Activity onDestroy
Pressiona back e sai da segunda activity
🔷Segundo Fragment onPause
🔵Segunda Activity onPause
🔶Primeiro Fragment onDestroyView
🔶Primeiro Fragment onDestroy
🔶Primeiro Fragment onDetach
🔴Primeira Activity onDestroy
🔶Primeiro Fragment onAttach
🔶Primeiro Fragment onCreate
🔶Primeiro Fragment onCreateView
🔴Primeira Activity onCreate(bundle)
🔶Primeiro Fragment onActivityCreated
🔶Primeiro Fragment onStart
🔴Primeira Activity onStart
🔴Primeira Activity onRestoreInstanceState(bundle)
🔴Primeira Activity onResume
🔶Primeiro Fragment onResume
🔷Segundo Fragment onStop
🔵Segunda Activity onStop
🔷Segundo Fragment onDestroyView
🔷Segundo Fragment onDestroy
🔷Segundo Fragment onDetach
🔵Segunda Activity onDestroy
Pressiona back e sai da app
🔶Primeiro Fragment onPause
🔴Primeira Activity onPause
🔶Primeiro Fragment onStop
🔴Primeira Activity onStop
🔶Primeiro Fragment onDestroyView
🔶Primeiro Fragment onDestroy
🔶Primeiro Fragment onDetach
🔴Primeira Activity onDestroy
Conclusões obtidas
Através desse estudo de caso, cheguei a conclusões e aprendizados que antes não sabia. Eles são:
#1 Callbacks paralelos
Alguns callbacks do ciclo de vida acontecem paralelamente entre dois ou mais components, sendo assim a ordem de alguns eventos não é garantida.
Ex: Quando navegamos e uma activity aparece sobre a outra.
Ex 2: Activities e fragments sendo inicializados ao mesmo tempo (perceba no exemplo abaixo):
Navegação para segunda activity 👇
Primeiro Fragment onPause
Primeira Activity onPause
Segundo Fragment onAttach
Segundo Fragment onCreate
Segundo Fragment onCreateView
Segunda Activity onCreate(null)
Segundo Fragment onActivityCreated
🔷Segundo Fragment onStart <
🔵Segunda Activity onStart <
🔵Segunda Activity onResume <
🔷Segundo Fragment onResume <
Primeiro Fragment onStop
Primeira Activity onStop
Apesar do fragment ter chamado o onStart() antes da activity, não necessariamente o onResume() do mesmo vai ser chamado antes…
#2 Callbacks em Activities no final stack
Nas mudanças de configuração (rotação da tela). As activities que estão na parte de trás da stack e não estão visíveis não invocam os callbacks de lifecycle imediatamente. Os callbacks só são invocados quando a activity se torna visível.
#3 Curiosidades e informações adicionais
- Ver sua barra de notificações não chama nenhum callback de ciclo de vida.
- Existe um architecture component do Google para facilitar o gerenciamento do ciclo de vida da sua aplicação.
- Definir um launch mode customizado para sua activity pode alterar significativamente o ciclo de vida da sua aplicação.
- Utilizar retenção de fragments (Retained Fragments) também pode alterar o ciclo de vida de sua aplicação.
- Se o método finish() for chamado dentro dos callbacks onCreate() ou onStart(). A activity vai pular algumas etapas do ciclo de vida, atenção em relação a isso! ⚠️
Então é isso! Espero ter ajudado de alguma forma com esse conhecimento. Qualquer feedback por favor escrever nos comentários abaixo! 😄