Conceitos e implementação do Jetpack Compose
À medida que o usuário navega no aplicativo, sai dele e retorna a ele, as instâncias Activity
no aplicativo transitam entre diferentes estados no ciclo de vida.
A classe Activity fornece vários callbacks que informam à atividade
quando um estado muda ou que o sistema está criando, interrompendo ou retomando uma
atividade ou destruindo o processo em que ela reside.
Dentro dos métodos de callback do ciclo de vida, você pode declarar como a atividade se comporta quando o usuário sai e retorna dela. Por exemplo, se estiver construindo um reprodutor de vídeos de transmissão em sequência, você pode pausar o vídeo e encerrar a conexão de rede quando o usuário alternar para outro app. Quando o usuário retornar, será possível reconectar a rede e permitir que ele reinicie o vídeo de onde parou.
Cada callback permite que você realize o trabalho específico adequado a determinada mudança de estado. Fazer o trabalho certo no momento apropriado e gerenciar as transições da maneira correta faz com que seu aplicativo seja mais robusto e tenha melhor desempenho. Por exemplo, uma boa implementação dos callbacks de ciclo de vida pode ajudar seu app a evitar o seguinte:
- Falhas se o usuário receber uma ligação ou mudar para outro app enquanto usa o seu.
- Consumo de recursos importantes do sistema quando o usuário não estiver usando ativamente o aplicativo.
- Perda do progresso do usuário se ele sair do aplicativo e retornar mais tarde.
- Falhas ou perda do progresso do usuário quando a tela girar entre as orientações paisagem e retrato.
Este documento explica detalhadamente o ciclo de vida da atividade. O documento começa descrevendo o paradigma do ciclo de vida. Em seguida, cada um dos callbacks é explicado: o que acontece internamente quando eles operam e o que você precisa implementar durante a execução deles.
Depois é apresentada a relação entre o estado da atividade e a vulnerabilidade de um processo que está sendo eliminado pelo sistema. Por fim, vários tópicos relacionados às transições entre os estados de atividade são discutidos.
Para informações sobre como gerenciar ciclos de vida, incluindo orientação sobre práticas recomendadas, consulte Como gerenciar ciclos de vida com componentes que os reconhecem e Salvar estados da interface. Para saber como arquitetar um app robusto e de qualidade de produção usando atividades em combinação com componentes de arquitetura, consulte o Guia para a arquitetura do app.
Conceitos do ciclo de vida da atividade
Para navegar entre as fases do ciclo de vida da atividade, a classe Activity
fornece um conjunto principal de seis callbacks: onCreate, onStart,
onResume, onPause, onStop e onDestroy. O sistema invoca cada um desses callbacks conforme a atividade entra em um novo estado.
A Figura 1 mostra uma representação visual desse paradigma.
À medida que o usuário começa a sair da atividade, o sistema chama métodos para eliminá-la. Em alguns casos, a atividade é apenas parcialmente eliminada e ainda reside na memória, como quando o usuário alterna para outro app. Nesses casos, a atividade ainda pode voltar ao primeiro plano.
Se o usuário retornar à atividade, ela será retomada de onde o usuário parou. Com algumas exceções, os apps são impedidos de iniciar atividades quando executados em segundo plano.
A probabilidade de o sistema eliminar um determinado processo, com as atividades nele, depende do estado da atividade no momento. Para mais informações sobre a relação entre o estado e a vulnerabilidade à ejeção, consulte a seção sobre estado da atividade e ejeção da memória.
Dependendo da complexidade de sua atividade, não é necessário implementar todos os métodos do ciclo de vida. No entanto, é importante entender cada um deles e implementar somente os que garantem que o aplicativo tenha o desempenho esperado pelo usuário.
Callbacks do ciclo de vida
Esta seção fornece informações conceituais e de implementação sobre os métodos de callback usados durante o ciclo de vida da atividade.
Algumas ações pertencem aos métodos do ciclo de vida da atividade. No entanto, coloque o código que implementa as ações de um componente dependente no componente, em vez do método de ciclo de vida da atividade. Para fazer isso, você precisa tornar o componente dependente ciente do ciclo de vida. Para saber como tornar seus componentes dependentes cientes do ciclo de vida, consulte Como gerenciar ciclos de vida com componentes que os reconhecem.
onCreate
Esse callback precisa ser implementado. Ele é acionado assim que o sistema cria a atividade. Quando a atividade é criada, ela insere o estado Criado. No método
onCreate, execute a lógica básica de inicialização do aplicativo. Isso acontece
somente uma vez durante todo o período que a atividade durar.
Por exemplo, sua implementação de onCreate pode vincular dados a listas, associar a atividade a um ViewModel e instanciar algumas variáveis com escopo de classe. Esse método recebe o parâmetro savedInstanceState, que é um objeto Bundle que contém o estado salvo anteriormente da atividade. Se a atividade nunca existiu, o valor do objeto Bundle será nulo.
Se você tiver um componente ciente do ciclo de vida conectado ao ciclo de vida da sua atividade, ele vai receber o evento ON_CREATE. O método anotado
com @OnLifecycleEvent é chamado para que seu componente ciente do ciclo de vida possa executar
qualquer código de configuração necessário para o estado criado.
O exemplo a seguir do método onCreate mostra uma configuração fundamental para a
atividade, como declarar a interface do usuário (definida em um arquivo de layout XML),
definir variáveis de associação e configurar parte da IU. Neste exemplo, o arquivo de layout XML passa o ID de recurso R.layout.main_activity do arquivo para setContentView.
Kotlin
lateinit var textView: TextView
// Some transient state for the activity instance.
var gameState: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
// Call the superclass onCreate to complete the creation of
// the activity, like the view hierarchy.
super.onCreate(savedInstanceState)
// Recover the instance state.
gameState = savedInstanceState?.getString(GAME_STATE_KEY)
// Set the user interface layout for this activity.
// The layout is defined in the project res/layout/main_activity.xml file.
setContentView(R.layout.main_activity)
// Initialize member TextView so it is available later.
textView = findViewById(R.id.text_view)
}
// This callback is called only when there is a saved instance previously saved using
// onSaveInstanceState(). Some state is restored in onCreate(). Other state can optionally
// be restored here, possibly usable after onStart() has completed.
// The savedInstanceState Bundle is same as the one used in onCreate().
override fun onRestoreInstanceState(savedInstanceState: Bundle?) {
textView.text = savedInstanceState?.getString(TEXT_VIEW_KEY)
}
// Invoked when the activity might be temporarily destroyed; save the instance state here.
override fun onSaveInstanceState(outState: Bundle?) {
outState?.run {
putString(GAME_STATE_KEY, gameState)
putString(TEXT_VIEW_KEY, textView.text.toString())
}
// Call superclass to save any view hierarchy.
super.onSaveInstanceState(outState)
}
Java
TextView textView;
// Some transient state for the activity instance.
String gameState;
@Override
public void onCreate(Bundle savedInstanceState) {
// Call the superclass onCreate to complete the creation of
// the activity, like the view hierarchy.
super.onCreate(savedInstanceState);
// Recover the instance state.
if (savedInstanceState != null) {
gameState = savedInstanceState.getString(GAME_STATE_KEY);
}
// Set the user interface layout for this activity.
// The layout is defined in the project res/layout/main_activity.xml file.
setContentView(R.layout.main_activity);
// Initialize member TextView so it is available later.
textView = (TextView) findViewById(R.id.text_view);
}
// This callback is called only when there is a saved instance previously saved using
// onSaveInstanceState(). Some state is restored in onCreate(). Other state can optionally
// be restored here, possibly usable after onStart() has completed.
// The savedInstanceState Bundle is same as the one used in onCreate().
@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
textView.setText(savedInstanceState.getString(TEXT_VIEW_KEY));
}
// Invoked when the activity might be temporarily destroyed; save the instance state here.
@Override
public void onSaveInstanceState(Bundle outState) {
outState.putString(GAME_STATE_KEY, gameState);
outState.putString(TEXT_VIEW_KEY, textView.getText());
// Call superclass to save any view hierarchy.
super.onSaveInstanceState(outState);
}
Como alternativa para definir o arquivo XML e passá-lo para
setContentView, você pode criar novos objetos View no seu código de
atividade e criar uma hierarquia de visualização inserindo novos objetos View em um
ViewGroup. Em seguida, você usará o layout passando a raiz ViewGroup para setContentView. Para mais informações sobre a criação de uma interface do usuário, consulte a documentação Interface do usuário.
Sua atividade não permanece no estado "Criado". Depois que o método onCreate
conclui a execução, a atividade entra no estado Iniciado e o sistema chama
os métodos onStart e onResume em rápida sucessão.
onStart
Quando a atividade entra no estado "Iniciado", o sistema invoca onStart.
Essa chamada torna a atividade visível ao usuário à medida que o app se prepara para que a
atividade entre em primeiro plano e se torne interativa. Por exemplo, é nesse método que o código que mantém a interface é inicializado.
Quando a atividade é movida para o estado "Iniciado", qualquer componente ciente do ciclo de vida ligado ao ciclo de vida da atividade recebe o evento ON_START.
O método onStart é concluído rapidamente e, como no estado "Criado", a atividade não permanece no estado "Iniciado". Quando a finalização é feita pelo callback, a
atividade insere o estado Retomado e o sistema invoca o método onResume.
onResume
Quando a atividade entra no estado "Retomado", ela vem para o primeiro plano, e o
sistema invoca o callback onResume. É nesse estado que o aplicativo interage com o usuário. O app permanece nesse estado até que algo afete o foco do app, como o dispositivo receber uma chamada telefônica, o usuário navegar para outra atividade ou a tela do dispositivo ser desativada.
Quando a atividade é movida para o estado "Retomado", qualquer componente ciente do ciclo de vida ligado ao ciclo de vida da atividade recebe o evento ON_RESUME. É nesse momento que os componentes do ciclo de vida podem ativar qualquer funcionalidade que precise operar enquanto o componente estiver visível e em primeiro plano, como o início da visualização da câmera.
Quando ocorre um evento de interrupção, a atividade entra no estado Pausado e
o sistema invoca o callback onPause.
Se a atividade retornar do estado "Pausado" para o estado "Retomado", o sistema
chamará novamente o método onResume. Por isso, implemente onResume para
inicializar os componentes liberados durante onPause e executar outras
inicializações que devem ocorrer sempre que a atividade entrar no estado "Retomado".
Este é um exemplo de componente ciente do ciclo de vida que acessa a câmera quando o componente recebe o evento ON_RESUME:
Kotlin
class CameraComponent : LifecycleObserver {
...
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
fun initializeCamera() {
if (camera == null) {
getCamera()
}
}
...
}
Java
public class CameraComponent implements LifecycleObserver {
...
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
public void initializeCamera() {
if (camera == null) {
getCamera();
}
}
...
}
O código anterior inicializa a câmera quando o LifecycleObserver
recebe o evento ON_RESUME. No entanto, no modo de várias janelas, a atividade
poderá ficar completamente visível mesmo quando estiver no estado "Pausado". Por exemplo, quando o app está no modo de várias janelas e o usuário toca na janela que não contém a atividade, essa atividade é movida para o estado "Pausado".
Se você quiser que a câmera seja ativada somente quando o app estiver Retomado (visível e ativo
em primeiro plano), inicialize a câmera após o evento ON_RESUME
demonstrado anteriormente. Se você quiser manter a câmera ativa enquanto a
atividade estiver pausada, mas visível, como no modo de várias janelas, inicialize
a câmera após o evento ON_START.
No entanto, deixar a câmera ativa quando a atividade estiver pausada pode negar o acesso de outro aplicativo retomado no modo de várias janelas à câmera. Às vezes, é necessário manter a câmera ativa enquanto sua atividade está pausada, mas isso pode prejudicar a experiência geral do usuário.
Por isso, pense cuidadosamente sobre onde, no ciclo de vida, é mais apropriado assumir o controle de recursos compartilhados do sistema no contexto do modo de várias janelas. Para saber mais sobre o suporte ao modo de várias janelas, consulte Ofereça suporte ao modo de várias janelas.
Independentemente de qual evento de construção você escolher para executar uma operação de inicialização, certifique-se de usar o evento de ciclo de vida correspondente para liberar o recurso. Se você inicializar algo após o evento ON_START, libere ou
finalize esse item após o evento ON_STOP. Se você inicializar após o evento ON_RESUME, faça a liberação após o evento ON_PAUSE.
O snippet de código anterior coloca o código de inicialização da câmera em um
componente ciente do ciclo de vida. Em vez disso, você pode colocar esse código diretamente nos callbacks do ciclo de vida da atividade, como onStart e onStop, mas isso não é recomendado. Adicionar essa lógica a um componente independente e ciente do ciclo de vida
permite reutilizar o componente em várias atividades sem ter que
duplicar o código. Para saber como criar um componente ciente do ciclo de vida, consulte
Como gerenciar ciclos de vida com componentes que os reconhecem (Views).
onPause
O sistema chama esse método como a primeira indicação de que o usuário está deixando sua atividade, embora nem sempre signifique que a atividade esteja sendo destruída. Isso indica que a atividade não está mais em primeiro plano, mas ainda está visível se o usuário estiver no modo de várias janelas. Há vários motivos para uma atividade entrar nesse estado:
- Um evento que interrompe a execução do app, conforme descrito na seção sobre
o callback
onResume, pausa a atividade atual. Esse é o caso mais comum. - No modo de várias janelas, apenas um app tem foco por vez, e o sistema pausa todos os outros apps.
- A abertura de uma nova atividade semitransparente, como uma caixa de diálogo, pausa a atividade que ela cobre. Enquanto a atividade estiver parcialmente visível, mas não em foco, ela permanecerá pausada.
Quando uma atividade é movida para o estado pausado, qualquer componente ciente do ciclo de vida ligado ao ciclo de vida da atividade recebe o evento ON_PAUSE. É nesse momento que os componentes do ciclo de vida podem interromper qualquer funcionalidade que não precise operar enquanto o componente não estiver em primeiro plano, como na pausa de uma visualização da câmera.
Use o método onPause para pausar ou ajustar operações que não podem continuar ou que podem continuar com moderação enquanto o Activity estiver no estado "Pausado" e que você espera retomar em breve.
Também é possível usar o método onPause para liberar recursos do sistema, tratamento de sensores (como GPS) ou quaisquer recursos que afetem a duração da bateria enquanto a atividade estiver pausada e o usuário não precisar deles.
No entanto, como mencionado na seção sobre onResume, uma atividade pausada ainda poderá ser completamente visível se o app estiver no modo de várias janelas. Considere usar
onStop em vez de onPause para liberar ou ajustar completamente recursos
e operações relacionadas à interface para melhorar o suporte do modo de várias janelas.
O exemplo a seguir de um LifecycleObserver reagindo ao evento ON_PAUSE é a contraparte do exemplo de evento ON_RESUME anterior, liberando a câmera que é inicializada após o recebimento do evento ON_RESUME:
Kotlin
class CameraComponent : LifecycleObserver {
...
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
fun releaseCamera() {
camera?.release()
camera = null
}
...
}
Java
public class JavaCameraComponent implements LifecycleObserver {
...
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
public void releaseCamera() {
if (camera != null) {
camera.release();
camera = null;
}
}
...
}
Este exemplo posiciona o código de liberação da câmera depois que o evento ON_PAUSE é
recebido pelo LifecycleObserver.
onPauseA execução é muito breve e não oferece necessariamente tempo suficiente para
realizar operações de salvamento. Por isso, não use onPause para salvar dados do aplicativo ou do usuário, fazer chamadas de rede ou executar transações de banco de dados.
Esse trabalho pode não ser concluído antes da conclusão do método.
Em vez disso, realize operações de desligamento pesadas durante onStop. Para mais informações sobre operações adequadas a serem realizadas durante onStop, consulte a próxima seção. Para mais informações sobre como salvar dados, consulte a seção sobre salvar
e restaurar estado.
A conclusão do método onPause não significa que a atividade saia do
estado "Pausado". Na verdade, a atividade permanece nesse estado até que ela seja retomada ou fique completamente invisível para o usuário. Se a atividade
for retomada, o sistema invocará mais uma vez o callback onResume.
Se a atividade retornar do estado "Pausado" para o estado "Retomado", o sistema
manterá a instância Activity residente na memória, chamando novamente a instância quando
o sistema invocar onResume. Nesse cenário, não é necessário reiniciar
componentes criados durante qualquer método de callback que leve ao estado
"Retomado". Se a atividade ficar completamente invisível, o sistema chamará onStop.
onStop
Quando a atividade não estiver mais visível ao usuário, ela entrará no estado Interrompido e o sistema invocará o callback onStop. Isso pode acontecer quando uma
atividade recém-lançada preenche toda a tela. O sistema também chama onStop
quando a atividade para de operar e está prestes a ser concluída.
Quando a atividade é movida para o estado "Interrompido", qualquer componente ciente do ciclo de vida ligado ao ciclo de vida da atividade recebe o evento ON_STOP. É nesse momento que os componentes do ciclo de vida podem interromper qualquer funcionalidade que não precise operar enquanto o componente não estiver visível na tela.
No método onStop, libere ou ajuste recursos desnecessários enquanto
o app não estiver visível ao usuário. Por exemplo, o app pode pausar animações
ou alternar de atualizações de local mais específicas para as menos detalhadas. O uso de onStop
em vez de onPause significa que o trabalho relacionado à interface continua, mesmo quando o
usuário está visualizando a atividade no modo de várias janelas.
Use onStop também para realizar operações de desligamento de uso intensivo da CPU. Por exemplo, se você não encontrar um momento melhor para salvar informações em um banco de dados, poderá fazer isso durante onStop. O exemplo abaixo mostra uma implementação de
onStop que salva o conteúdo de uma nota de rascunho no armazenamento persistente:
Kotlin
override fun onStop() {
// Call the superclass method first.
super.onStop()
// Save the note's current draft, because the activity is stopping
// and we want to be sure the current note progress isn't lost.
val values = ContentValues().apply {
put(NotePad.Notes.COLUMN_NAME_NOTE, getCurrentNoteText())
put(NotePad.Notes.COLUMN_NAME_TITLE, getCurrentNoteTitle())
}
// Do this update in background on an AsyncQueryHandler or equivalent.
asyncQueryHandler.startUpdate(
token, // int token to correlate calls
null, // cookie, not used here
uri, // The URI for the note to update.
values, // The map of column names and new values to apply to them.
null, // No SELECT criteria are used.
null // No WHERE columns are used.
)
}
Java
@Override
protected void onStop() {
// Call the superclass method first.
super.onStop();
// Save the note's current draft, because the activity is stopping
// and we want to be sure the current note progress isn't lost.
ContentValues values = new ContentValues();
values.put(NotePad.Notes.COLUMN_NAME_NOTE, getCurrentNoteText());
values.put(NotePad.Notes.COLUMN_NAME_TITLE, getCurrentNoteTitle());
// Do this update in background on an AsyncQueryHandler or equivalent.
asyncQueryHandler.startUpdate (
mToken, // int token to correlate calls
null, // cookie, not used here
uri, // The URI for the note to update.
values, // The map of column names and new values to apply to them.
null, // No SELECT criteria are used.
null // No WHERE columns are used.
);
}
O exemplo de código anterior usa o SQLite diretamente. No entanto, recomendamos usar o Room, uma biblioteca de persistência que oferece uma camada de abstração acima do SQLite. Para saber mais sobre os benefícios de usar o Room e como implementá-lo no seu app, consulte o guia biblioteca de persistência do Room.
Quando sua atividade entra no estado "Parado", o objeto Activity é mantido residente na memória: ele mantém todas as informações de estado e membro, mas não é anexado ao gerenciador de janelas. Quando a atividade é retomada, ela chama novamente essas
informações.
Não é necessário reiniciar componentes criados durante qualquer método de callback
que leve ao estado "Retomado". O sistema também acompanha o estado atual de cada objeto View no layout. Portanto, se o usuário inserir um texto em um widget EditText, o conteúdo será retido e você não precisará salvar e restaurar.
A partir do estado "Interrompido", a atividade volta a interagir com o usuário ou para de operar e é encerrada. Se a atividade voltar, o sistema invocará onRestart. Se a Activity deixar de operar, o sistema chamará onDestroy.
onDestroy
onDestroy é chamado antes de a atividade ser destruída. O sistema invoca
esse callback por um destes dois motivos:
- A atividade está sendo finalizada porque o usuário a descartou completamente ou porque
finishfoi chamado nela. - O sistema está destruindo temporariamente a atividade devido a uma mudança na configuração, como a rotação do dispositivo ou a entrada no modo de várias janelas.
Quando a atividade é movida para o estado destruído, qualquer componente ciente do ciclo de vida
ligado ao ciclo de vida da atividade recebe o evento ON_DESTROY. É nesse momento que os componentes do ciclo de vida podem limpar qualquer item que eles precisarem antes da destruição do Activity.
Em vez de inserir lógica na sua Activity para determinar por que ela está sendo
destruída, use um objeto ViewModel para manter os dados de visualização relevantes para
sua Activity. Se o Activity for recriado devido a uma mudança na configuração,
o ViewModel não precisará fazer nada, já que será preservado e fornecido à
próxima instância de Activity.
Se a Activity não for recriada, a ViewModel vai chamar o método
onCleared, em que ela pode limpar os dados necessários
antes da destruição. É possível distinguir entre essas duas situações com o método isFinishing.
Caso a atividade esteja sendo encerrada, onDestroy será o callback do ciclo de vida final recebido pela atividade. Se onDestroy for chamado como resultado de uma mudança na configuração, o sistema criará imediatamente uma nova instância de atividade e chamará onCreate nessa instância na nova configuração.
O callback onDestroy libera todos os recursos não liberados por callbacks anteriores, como onStop.
Como salvar e restaurar o estado transitório da IU
O usuário espera que o estado da interface de uma atividade permaneça o mesmo durante uma mudança de configuração, como girar ou mudar o dispositivo para o modo de várias janelas. No entanto, o sistema destrói a atividade por padrão quando ocorre uma mudança de configuração. Isso exclui qualquer estado de IU armazenado na instância da atividade.
Da mesma forma, um usuário espera que o estado da interface permaneça o mesmo se ele alternar temporariamente do seu app para outro e retornar ao inicial depois. No entanto, o sistema pode destruir o processo do aplicativo enquanto o usuário estiver ausente e a atividade for interrompida.
Quando limitações do sistema destroem a atividade, use uma combinação de ViewModel, onSaveInstanceState e/ou armazenamento local para preservar o estado transitório da UI do usuário. Para saber mais sobre as expectativas do usuário em relação ao comportamento do sistema e como preservar melhor os dados complexos do estado da interface nas atividades iniciadas pelo sistema e encerramento do processo, consulte Como salvar estados da interface.
Essa seção destaca qual o estado da instância e como implementar o método onSaveInstance, que é por si só um callback na atividade. Se os dados da sua UI forem leves, você poderá usar onSaveInstance sozinho para manter o estado da UI nas mudanças de configuração e no encerramento do processo iniciado pelo sistema. No entanto, como onSaveInstance incorre em custos de serialização/desserialização, na maioria dos casos, você usa ViewModel e onSaveInstance, conforme descrito em Salvar estados da interface.
Estado da instância
Há alguns casos em que a atividade é destruída devido ao comportamento normal do app, como quando o usuário pressiona o botão "Voltar" ou a atividade sinaliza a própria destruição chamando o método finish.
Quando a atividade é destruída porque o usuário pressionou o botão "Voltar" ou a atividade
é finalizada, o conceito do sistema e do usuário dessa
instância Activity se perde. Nesses cenários, a expectativa do usuário corresponde ao comportamento do sistema, e você não tem trabalho extra.
No entanto, se o sistema destruir a atividade devido a limitações dele mesmo (como uma mudança na configuração ou pressão sobre a memória), embora a instância Activity real tenha se perdido, o sistema recordará a existência dela. Se o usuário tentar
navegar de volta para a atividade, o sistema criará uma nova instância dela usando um conjunto de dados salvos que descrevem o estado da atividade quando
ela foi destruída.
Os dados salvos que o sistema usa para restaurar o estado anterior são chamados de
estado da instância. É uma coleção de pares de chave-valor armazenados em um objeto
Bundle. Por padrão, o sistema usa o estado da instância Bundle para salvar informações sobre cada objeto View no layout da atividade, como o valor de texto inserido em um widget EditText.
Assim, se a instância da atividade for destruída e recriada, o estado do layout será restaurado para o estado anterior sem que haja necessidade de código. No entanto, a atividade pode conter mais informações de estado do que se quer restaurar, como variáveis de associação que rastreiam o progresso do usuário na atividade.
Um objeto Bundle não é adequado para preservar mais do que uma quantidade trivial de dados, porque requer serialização no thread principal e consome memória do processo do sistema. Para preservar mais do que uma quantidade muito pequena de dados, use uma abordagem combinada para preservar dados, com armazenamento local, o método onSaveInstanceState e a classe ViewModel, conforme destacado em Salvar estados da interface.
Salvar estados de UI leves e simples com onSaveInstanceState
À medida que a atividade começa a parar, o sistema chama o método
onSaveInstanceState para que ela possa salvar informações de estado
em um pacote de estado da instância. A implementação padrão desse método salva informações transitórias sobre o estado da hierarquia de visualização da atividade, como o texto em um widget EditText ou a posição de rolagem de um widget ListView.
Para salvar informações sobre o estado da instância adicional para a atividade, substitua
onSaveInstanceState e adicione pares de chave-valor ao objeto Bundle que é
salvo no evento em que a atividade é inesperadamente destruída. Ao
modificar onSaveInstanceState, você precisa chamar a implementação de superclasse
se quiser que a implementação padrão salve o estado da hierarquia de visualização.
Isso é mostrado neste exemplo:
Kotlin
override fun onSaveInstanceState(outState: Bundle?) {
// Save the user's current game state.
outState?.run {
putInt(STATE_SCORE, currentScore)
putInt(STATE_LEVEL, currentLevel)
}
// Always call the superclass so it can save the view hierarchy state.
super.onSaveInstanceState(outState)
}
companion object {
val STATE_SCORE = "playerScore"
val STATE_LEVEL = "playerLevel"
}
Java
static final String STATE_SCORE = "playerScore";
static final String STATE_LEVEL = "playerLevel";
// ...
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
// Save the user's current game state.
savedInstanceState.putInt(STATE_SCORE, currentScore);
savedInstanceState.putInt(STATE_LEVEL, currentLevel);
// Always call the superclass so it can save the view hierarchy state.
super.onSaveInstanceState(savedInstanceState);
}
Para salvar os dados persistentes, como as preferências do usuário ou dados para um banco de dados, aproveite as oportunidades adequadas quando a atividade estiver em primeiro plano. Se essa oportunidade não surgir, salve os dados persistentes durante o método onStop.
Restaurar o estado de IU da atividade por meio de um estado da instância salvo
Quando a atividade é recriada depois de ter sido destruída, é possível recuperar o estado salvo do Bundle que o sistema passou para a atividade. Os métodos de callback onCreate e onRestoreInstanceState
recebem o mesmo Bundle que contém informações do estado da
instância.
Como o método onCreate é chamado se o sistema estiver criando uma nova
instância da atividade ou recriando uma anterior, verifique
se o Bundle de estado é nulo antes de tentar realizar a leitura. Se for nulo, o sistema criará uma nova instância da atividade em vez de restaurar uma anterior que tenha sido destruída.
O snippet de código a seguir mostra como restaurar alguns dados de estado em
onCreate:
Kotlin
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) // Always call the superclass first
// Check whether we're recreating a previously destroyed instance.
if (savedInstanceState != null) {
with(savedInstanceState) {
// Restore value of members from saved state.
currentScore = getInt(STATE_SCORE)
currentLevel = getInt(STATE_LEVEL)
}
} else {
// Probably initialize members with default values for a new instance.
}
// ...
}
Java
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); // Always call the superclass first
// Check whether we're recreating a previously destroyed instance.
if (savedInstanceState != null) {
// Restore value of members from saved state.
currentScore = savedInstanceState.getInt(STATE_SCORE);
currentLevel = savedInstanceState.getInt(STATE_LEVEL);
} else {
// Probably initialize members with default values for a new instance.
}
// ...
}
Em vez de restaurar o estado durante onCreate, você pode implementar
onRestoreInstanceState, que o sistema chama após o método onStart. O sistema chama onRestoreInstanceState somente se houver um estado salvo
a ser restaurado. Portanto, não é necessário verificar se Bundle é nulo.
Kotlin
override fun onRestoreInstanceState(savedInstanceState: Bundle?) {
// Always call the superclass so it can restore the view hierarchy.
super.onRestoreInstanceState(savedInstanceState)
// Restore state members from saved instance.
savedInstanceState?.run {
currentScore = getInt(STATE_SCORE)
currentLevel = getInt(STATE_LEVEL)
}
}
Java
public void onRestoreInstanceState(Bundle savedInstanceState) {
// Always call the superclass so it can restore the view hierarchy.
super.onRestoreInstanceState(savedInstanceState);
// Restore state members from saved instance.
currentScore = savedInstanceState.getInt(STATE_SCORE);
currentLevel = savedInstanceState.getInt(STATE_LEVEL);
}
Como navegar entre as atividades
É provável que um app entre e saia de uma atividade, talvez muitas vezes, durante a vida útil do app, como quando o usuário toca no botão "Voltar" do dispositivo ou a atividade inicia outra atividade.
Esta seção aborda os tópicos que você precisa conhecer para implementar transições de atividades bem-sucedidas. Esses tópicos incluem iniciar uma atividade a partir de outra, salvar e restaurar o estado da atividade.
Como iniciar uma atividade a partir de outra
Uma atividade frequentemente precisa iniciar outra em algum momento. Essa necessidade surge, por exemplo, quando um app precisa ser movido da tela atual para uma nova.
Dependendo se sua atividade quer um resultado da atividade que está prestes a iniciar, inicie a nova atividade usando o método startActivity ou startActivityForResult. De qualquer forma, passe um objeto Intent.
O objeto Intent especifica a atividade exata a ser iniciada ou descreve o tipo de ação que ela deve executar. O sistema seleciona a atividade adequada, que pode ser até de um outro aplicativo. Um objeto
Intent também pode carregar pequenas quantidades de dados que serão usados pela atividade
iniciada. Para mais informações sobre a classe Intent, consulte Intents e filtros de intents.
startActivity
Se a atividade recém-criada não precisar retornar um resultado, a atividade atual poderá iniciá-la chamando o método startActivity.
Ao trabalhar no aplicativo, frequentemente será necessário iniciar uma
atividade conhecida. Por exemplo, o snippet de código a seguir mostra como iniciar uma
atividade chamada SignInActivity.
Kotlin
val intent = Intent(this, SignInActivity::class.java)
startActivity(intent)
Java
Intent intent = new Intent(this, SignInActivity.class);
startActivity(intent);
Seu aplicativo também pode precisar realizar alguma ação, como enviar um e-mail, uma mensagem de texto ou atualização de status, usando os dados da atividade. Nesse caso, o aplicativo pode não ter as próprias atividades para executar esse tipo de ação. Assim, você pode aproveitar as atividades fornecidas por outros aplicativos do dispositivo que podem executar essas ações.
É aqui que as intenções são muito úteis. É possível criar uma intent que descreva uma ação a ser executada para que o sistema inicie a atividade apropriada de outro aplicativo. Se houver mais de uma atividade que possa processar a intent, o usuário poderá escolher qual usar. Por exemplo, se quiser que o usuário envie uma mensagem de e-mail, é possível criar a seguinte intent:
Kotlin
val intent = Intent(Intent.ACTION_SEND).apply {
putExtra(Intent.EXTRA_EMAIL, recipientArray)
}
startActivity(intent)
Java
Intent intent = new Intent(Intent.ACTION_SEND);
intent.putExtra(Intent.EXTRA_EMAIL, recipientArray);
startActivity(intent);
O extra EXTRA_EMAIL adicionado à intent é uma matriz de strings de endereços de e-mail
a que o e-mail será enviado. Quando um aplicativo de e-mail responde a essa intent, ele lê a matriz de strings fornecida no extra e coloca os endereços no campo "para" do formulário de composição de e-mail. Nessa situação, a atividade do aplicativo de e-mails começa e, quando o usuário termina o trabalho, sua atividade é retomada.
startActivityForResult
Às vezes, você precisa receber um resultado de uma atividade quando ela é encerrada. Por exemplo, você pode iniciar uma atividade que permite ao usuário escolher uma pessoa em uma lista de contatos. Quando a atividade é encerrada, ela mostra a pessoa selecionada. Para fazer isso, chame o método startActivityForResult(Intent, int), em que o parâmetro inteiro identifica a chamada.
Esse identificador tem o objetivo de distinguir entre várias chamadas para
startActivityForResult(Intent, int) da mesma atividade. Ele não é um identificador global e não corre o risco de entrar em conflito com outros apps ou atividades. O
resultado é retornado pelo método onActivityResult(int, int, Intent).
Quando uma atividade filha é encerrada, ela pode chamar setResult(int) para retornar dados à atividade
pai. A atividade filha precisa fornecer um código de resultado, que pode ser os resultados padrão RESULT_CANCELED, RESULT_OK ou quaisquer outros valores personalizados que comecem com RESULT_FIRST_USER.
Além disso, a atividade filha tem a opção de retornar um objeto Intent que contenha quaisquer dados adicionais que ela quiser. A atividade pai usa o método
onActivityResult(int, int, Intent), com o identificador inteiro
que ela forneceu originalmente, para receber a informação.
Se houver uma falha na atividade filha por qualquer motivo, a atividade mãe
receberá um resultado com o código RESULT_CANCELED.
Kotlin
class MyActivity : Activity() {
// ...
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
// When the user center presses, let them pick a contact.
startActivityForResult(
Intent(Intent.ACTION_PICK,Uri.parse("content://contacts")),
PICK_CONTACT_REQUEST)
return true
}
return false
}
override fun onActivityResult(requestCode: Int, resultCode: Int, intent: Intent?) {
when (requestCode) {
PICK_CONTACT_REQUEST ->
if (resultCode == RESULT_OK) {
// A contact was picked. Display it to the user.
startActivity(Intent(Intent.ACTION_VIEW, intent?.data))
}
}
}
companion object {
internal val PICK_CONTACT_REQUEST = 0
}
}
Java
public class MyActivity extends Activity {
// ...
static final int PICK_CONTACT_REQUEST = 0;
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
// When the user center presses, let them pick a contact.
startActivityForResult(
new Intent(Intent.ACTION_PICK,
new Uri("content://contacts")),
PICK_CONTACT_REQUEST);
return true;
}
return false;
}
protected void onActivityResult(int requestCode, int resultCode,
Intent data) {
if (requestCode == PICK_CONTACT_REQUEST) {
if (resultCode == RESULT_OK) {
// A contact was picked. Display it to the user.
startActivity(new Intent(Intent.ACTION_VIEW, data));
}
}
}
}
Coordenação de atividades
Quando uma atividade inicia outra, ambas passam por transições no ciclo de vida. A primeira atividade para de operar e entra no estado "Pausado" ou "Interrompido" enquanto a outra atividade é criada. Caso essas atividades compartilhem dados salvos em disco ou em outro lugar, é importante compreender que a primeira atividade não é totalmente interrompida antes da criação da segunda. Em vez disso, o processo de iniciar a segunda se sobrepõe ao processo de interromper a primeira.
A ordem dos callbacks do ciclo de vida é bem definida, especialmente quando as duas atividades estão no mesmo processo (ou seja, no mesmo app) e uma está iniciando a outra. Esta é a ordem das operações que ocorrem quando a atividade A inicia a atividade B:
- O método
onPauseda atividade A é executado. - Os métodos
onCreate,onStarteonResumeda atividade B são executados em sequência. A atividade B agora tem o foco do usuário. - Se a atividade A não estiver mais visível na tela, o método
onStopserá executado.
Essa sequência de callbacks do ciclo de vida permite gerenciar a transição de informações de uma atividade para outra.