Ciclo de vida da atividade

À 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 permitem que a atividade saiba quando um estado muda ou que o sistema está criando, interrompendo ou retomando uma atividade ou destruindo o processo em que ela está localizada.

Dentro dos métodos de callback do ciclo de vida, você pode declarar como a atividade deve se comportar quando o usuário sai e retorna dela. Por exemplo, se você estiver criando um player de streaming de vídeo, poderá pausar o vídeo e encerrar a conexão de rede quando o usuário alternar para outro app. Quando o usuário retornar, você poderá se reconectar à rede e permitir que ele retome o vídeo no mesmo local.

Cada callback permite realizar um trabalho específico adequado a uma 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 do ciclo de vida pode ajudar seu app a evitar o seguinte:

  • Falhas se o usuário receber uma chamada telefônica ou mudar para outro aplicativo enquanto estiver usando seu aplicativo.
  • 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 orientação da tela mudar entre 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, explica cada um dos callbacks: o que acontece internamente enquanto eles são executados e o que você precisa implementar durante a execução.

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 serão discutidos.

Para mais informações sobre como processar ciclos de vida, incluindo orientações sobre práticas recomendadas, consulte Como gerenciar ciclos de vida com componentes que os reconhecem e Salvar estados da interface. Para aprender a arquitetar um app robusto com qualidade de produção usando atividades em combinação com os componentes da arquitetura, consulte o Guia para a arquitetura do app.

Conceitos do ciclo de vida da atividade

Para navegar por transições entre os estágios 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 à medida que a atividade entra em um novo estado.

A imagem 1 demonstra a representação visual desse paradigma.

Figura 1. Uma ilustração simplificada do ciclo de vida da atividade.

À 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 desativada 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 durante a execução em segundo plano.

A probabilidade de o sistema encerrar um determinado processo, assim como as atividades nele, depende do estado da atividade no momento. Para saber mais sobre a relação entre estado e vulnerabilidade à expulsão, consulte a seção sobre estado da atividade e expulsã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 aqueles que fazem com que seu app se comporte da maneira esperada pelos usuários.

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 do 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 que acontece apenas uma vez durante toda a vida útil da atividade.

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 com reconhecimento de ciclo de vida conectado ao ciclo de vida da atividade, ele receberá o evento ON_CREATE. O método anotado com @OnLifecycleEvent é chamado para que seu componente com reconhecimento de 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 membro e configurar parte da interface. Neste exemplo, o arquivo de layout XML transmite 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 à definição do arquivo XML e à transmissão dele para setContentView(), você pode criar novos objetos View no código de atividade e criar uma hierarquia de visualização inserindo novos objetos View em um ViewGroup. Em seguida, você vai usar esse layout transmitindo a ViewGroup raiz para setContentView(). Para mais informações sobre como criar uma interface do usuário, consulte a documentação da interface do usuário.

A atividade não permanece no estado "Criado". Depois que o método onCreate() termina a execução, a atividade entra no estado Started 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 para o usuário enquanto 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 IU é inicializado.

Quando a atividade é movida para o estado "Iniciado", qualquer componente ciente do ciclo de vida vinculado ao ciclo de vida da atividade recebe o evento ON_START.

O método onStart() é concluído rapidamente e, assim como no estado "Criado", a atividade não permanece no estado "Iniciado". Quando esse callback é concluído, 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 aconteça para tirar o foco do app, por exemplo, o dispositivo receber uma ligação, o usuário navegar para outra atividade ou desligar a tela do dispositivo.

Quando a atividade é movida para o estado "Retomado", qualquer componente ciente do ciclo de vida vinculado 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 esse motivo, implemente onResume() para inicializar os componentes liberados durante onPause() e para executar outras inicializações que precisam ocorrer sempre que a atividade entrar no estado "Retomado".

Confira um exemplo de componente com reconhecimento de 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 pode ficar totalmente 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, ela passa para o estado "Pausado".

Se quiser que a câmera fique ativa 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, manter a câmera ativa enquanto a atividade está pausada pode negar o acesso de outro app retomado no modo de várias janelas à câmera. Às vezes, é necessário manter a câmera ativa enquanto a atividade está pausada, mas, se você fizer isso, a experiência geral do usuário poderá ser prejudicada.

Por esse motivo, pense com cuidado sobre o local do ciclo de vida em que é mais apropriado assumir o controle dos 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 Suporte a 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 encerre-o 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 com reconhecimento de ciclo de vida. Em vez disso, é possível colocar esse código diretamente nos callbacks do ciclo de vida da atividade, como onStart() e onStop(), mas não recomendamos isso. Adicionar essa lógica a um componente independente e com reconhecimento de ciclo de vida permite reutilizar o componente em várias atividades sem precisar duplicar o código. Para saber como criar um componente com reconhecimento de ciclo de vida, consulte Como gerenciar ciclos de vida com componentes que reconhecem o ciclo de vida.

onPause()

O sistema chama esse método como o primeiro indício de que o usuário está saindo da atividade, embora nem sempre isso signifique que a atividade vai ser destruída. Isso indica que a atividade não está mais em primeiro plano, mas ainda estará 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 fica em foco por vez, e o sistema pausa todos os outros.
  • A abertura de uma nova atividade semitransparente, como uma caixa de diálogo, pausa a atividade que ela abrange. Enquanto a atividade estiver parcialmente visível, mas não for o foco, ela permanecerá pausada.

Quando uma atividade é movida para o estado "Pausado", qualquer componente ciente do ciclo de vida vinculado 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 está no estado pausado, e que você espera que sejam retomadas em breve.

Você também pode usar o método onPause() para liberar recursos do sistema, identificadores de sensores (como GPS) ou qualquer recurso que afete a duração da bateria enquanto a atividade está pausada e o usuário não precisa deles.

No entanto, como mencionado na seção sobre onResume(), uma atividade pausada ainda poderá estar totalmente visível se o app estiver no modo de várias janelas. Considere usar onStop() em vez de onPause() para liberar ou ajustar completamente as operações e os recursos relacionados à interface para oferecer melhor suporte ao modo de várias janelas.

O exemplo a seguir de uma reação de LifecycleObserver 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 coloca o código de lançamento da câmera após o evento ON_PAUSE ser recebido pelo LifecycleObserver.

A execução de onPause() é 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, execute operações de desligamento pesadas durante onStop(). Para mais informações sobre operações adequadas a serem executadas durante onStop(), consulte a próxima seção. Para mais informações sobre como salvar dados, consulte a seção sobre como salvar e restaurar o estado.

A conclusão do método onPause() não significa que a atividade saia do estado "Pausado". Em vez disso, a atividade permanece nesse estado até que ela seja retomada ou se torne 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 essa instância quando o sistema invocar onResume(). Nesse cenário, não é necessário reiniciar componentes criados durante nenhum dos métodos de callback que levam ao estado "Retomado". Se a atividade ficar completamente invisível, o sistema chamará onStop().

onStop()

Quando a atividade não está mais visível para o usuário, ela entra no estado Interrompido e o sistema invoca o callback onStop(). Isso pode ocorrer quando uma atividade recém-iniciada cobre toda a tela. O sistema também chama onStop() quando a atividade termina de ser executada e está prestes a ser encerrada.

Quando a atividade é movida para o estado "Interrompido", qualquer componente com reconhecimento de ciclo de vida vinculado 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 que não são necessários enquanto o app não estiver visível para o usuário. Por exemplo, o aplicativo poderá 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 continuará, mesmo quando o usuário estiver visualizando a atividade no modo de várias janelas.

Além disso, use onStop() para executar operações de desligamento relativamente intensas da CPU. Por exemplo, se você não puder encontrar um horário 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 o uso do Room, uma biblioteca de persistência que fornece uma camada de abstração sobre o 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 Room.

Quando a atividade entra no estado "Interrompido", o objeto Activity é mantido na memória: ele mantém todas as informações de estado e de membro, mas não é anexado ao gerenciador de janelas. Quando a atividade é retomada, ela chama essas informações.

Não é necessário reinicializar componentes criados durante qualquer um dos métodos de callback que levem ao estado "Retomado". O sistema também monitora o estado atual de cada objeto View no layout. Portanto, se o usuário inserir texto em um widget EditText, esse conteúdo será retido, e você não precisará salvá-lo e restaurá-lo.

Observação: quando a atividade for interrompida, o sistema poderá destruir o processo que contém a atividade se precisar recuperar a memória. Mesmo que o sistema destrua o processo enquanto a atividade está interrompida, ele ainda mantém o estado dos objetos View, como texto em um widget EditText, em um Bundle (um blob de pares de chave-valor) e os restaura caso o usuário navegue de volta à atividade. Para mais informações sobre como restaurar uma atividade a que um usuário retorna, consulte a seção sobre como salvar e restaurar o estado.

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(). Caso a Activity deixe 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:

  1. A atividade está sendo concluída porque o usuário descartou completamente a atividade ou porque finish() está sendo chamado na atividade.
  2. 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 vinculado ao ciclo de vida da atividade recebe o evento ON_DESTROY. É nesse momento que os componentes do ciclo de vida podem limpar tudo o que precisam antes que o Activity seja destruído.

Em vez de colocar lógica na Activity para determinar por que ela está sendo destruída, use um objeto ViewModel para conter os dados de visualização relevantes para a Activity. Se a Activity for recriada devido a uma mudança de configuração, a ViewModel não precisará fazer nada, já que será preservada e fornecida à próxima instância do Activity.

Se a Activity não for recriada, a ViewModel terá o método onCleared() chamado, em que pode limpar todos os dados necessários antes de ser destruídos. É possível distinguir entre esses dois cenários com o método isFinishing().

Se a atividade estiver sendo finalizada, onDestroy() será o callback final do ciclo de vida que a atividade receberá. Se onDestroy() for chamado como resultado de uma mudança de configuração, o sistema vai 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().

Estado da atividade e ejeção da memória

O sistema elimina processos quando precisa liberar RAM. A probabilidade de o sistema encerrar um determinado processo depende do estado em que ele ocorreu. O estado do processo, por sua vez, depende do estado da atividade em execução no processo. A tabela 1 mostra as correlações entre o estado do processo, o estado da atividade e a probabilidade de o sistema eliminar o processo. Esta tabela se aplica apenas se um processo não estiver executando outros tipos de componentes de aplicativo.

Probabilidade de eliminação Estado do processo Estado da atividade final
Mínimo Em primeiro plano (com foco ou prestes a ter) Retomado
Baixo Visível (sem foco) Iniciado/pausado
Alta Segundo plano (invisível) Parado
Máximo Vazio Destruído

Tabela 1. Relação entre ciclo de vida do processo e estado da atividade.

O sistema nunca elimina uma atividade diretamente para liberar memória. Em vez disso, ele elimina o processo em que a atividade é executada, destruindo não apenas a atividade, mas também todo o restante em execução no processo. Para aprender a preservar e restaurar o estado da interface da sua atividade quando ocorre o encerramento do processo iniciado pelo sistema, consulte a seção sobre como salvar e restaurar o estado.

O usuário também pode encerrar um processo usando o Gerenciador de aplicativos, em "Configurações", para encerrar o app correspondente.

Para mais informações sobre processos, consulte Visão geral dos processos e linhas de execução.

Como salvar e restaurar o estado transitório da IU

O usuário espera que o estado da IU de uma atividade permaneça o mesmo durante uma mudança de configuração, como rotação ou alternar 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 IU permaneça o mesmo se ele alternar temporariamente do seu aplicativo para outro e retornar ao inicial posteriormente. No entanto, o sistema pode destruir o processo do aplicativo enquanto o usuário está ausente e a atividade é interrompida.

Quando as restrições do sistema destruir a atividade, preserve o estado transitório da interface do usuário usando uma combinação de ViewModel, onSaveInstanceState() e/ou armazenamento local. Para saber mais sobre as expectativas do usuário em comparação com o comportamento do sistema e como preservar melhor os dados complexos de estado da interface em atividades iniciadas pelo sistema e encerramento de processo, consulte Salvar estados da interface.

Esta seção descreve o que é o estado da instância e como implementar o método onSaveInstance(), que é um callback na própria atividade. Se os dados da interface forem leves, você poderá usar a onSaveInstance() sozinha para manter o estado da interface nas mudanças de configuração e no encerramento do processo iniciado pelo sistema. No entanto, como onSaveInstance() gera custos de serialização/desserialização, na maioria dos casos, você usa ViewModel e onSaveInstance(), conforme descrito em Salvar estados da interface.

Observação : para saber mais sobre mudanças de configuração, como restringir a recriação de atividades, se necessário, e como reagir a essas mudanças de configuração do sistema de visualização e do Jetpack Compose, consulte a página Processar mudanças de configuração.

Estado da instância

Há alguns casos em que a atividade é destruída devido ao comportamento normal do aplicativo, 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 pressiona "Voltar" ou a atividade termina sozinha, o conceito do sistema e do usuário sobre essa instância de Activity desaparece para sempre. Nesses cenários, a expectativa do usuário corresponde ao comportamento do sistema, e você não tem nenhum trabalho extra a fazer.

No entanto, se o sistema destruir a atividade devido a restrições dele, como uma mudança de configuração ou pressão de memória, embora a instância Activity real tenha se perdido, o sistema vai lembrar que ela existia. 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. Ele é 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.

Observação : para que o sistema Android restaure o estado das visualizações na sua atividade, cada uma delas precisa ter um ID exclusivo, fornecido pelo atributo android:id.

Um objeto Bundle não é adequado para preservar mais do que uma quantidade trivial de dados, porque exige serialização na linha de execução principal e consome memória do processo do sistema. Para preservar mais do que uma quantidade muito pequena de dados, adote uma abordagem combinada para preservar dados usando o armazenamento local permanente, o método onSaveInstanceState() e a classe ViewModel, conforme descrito em Salvar estados da interface.

Salvar estados de IU leves e simples com onSaveInstanceState()

À medida que a atividade começar a parar, o sistema chama o método onSaveInstanceState() para que ela possa salvar a informação do 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 ListView.

Para salvar outras informações sobre o estado da instância para a atividade, substitua onSaveInstanceState() e adicione pares de chave-valor ao objeto Bundle salvo caso sua atividade seja destruída inesperadamente. Ao substituir onSaveInstanceState(), você precisa chamar a implementação da 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);
}

Observação : onSaveInstanceState() não é chamado quando o usuário fecha explicitamente a atividade ou em outros casos em que finish() é chamado.

Para salvar dados persistentes, como preferências do usuário ou dados para um banco de dados, aproveite as oportunidades adequadas quando a atividade estiver em primeiro plano. Se essas oportunidades não surgirem, 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 as informações do estado da instância.

Como o método onCreate() é chamado quando o sistema está criando uma nova instância da atividade ou recriando uma anterior, é necessário verificar se o estado Bundle é nulo antes de tentar fazer 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 abaixo 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 optar por implementar onRestoreInstanceState(), que o sistema chama após o método onStart(). O sistema chamará onRestoreInstanceState() somente se houver um estado salvo para restaurar. 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);
}

Cuidado: sempre chame a implementação da superclasse de onRestoreInstanceState() para que a implementação padrão possa restaurar o estado da hierarquia de visualização.

Como navegar entre as atividades

É provável que um app entre e saia de uma atividade, talvez muitas vezes, durante o ciclo de vida do app, como quando o usuário toca no botão "Voltar" do dispositivo ou quando a atividade inicia uma atividade diferente.

Esta seção aborda os tópicos que você precisa conhecer para implementar transições de atividades corretamente. 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 aplicativo precisa ser movido da tela atual para uma nova.

Dependendo se a atividade quer ou não um resultado da nova 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 que você quer iniciar ou descreve o tipo de ação que você quer realizar. O sistema seleciona a atividade adequada para você, que pode até ser de um aplicativo diferente. 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-iniciada 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 intents são muito valiosas. É possível criar uma intent que descreva uma ação a ser realizada para que o sistema inicie a atividade adequada de outro aplicativo. Se houver mais de uma atividade que possa processar a intent, o usuário poderá escolher qual usará. Por exemplo, se você quiser permitir que o usuário envie uma mensagem de e-mail, crie a intent abaixo:

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 para onde 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-mail começa e, quando o usuário termina, 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 que o usuário escolha uma pessoa em uma lista de contatos. Quando termina, ele retorna a pessoa que foi selecionada. Para fazer isso, chame o método startActivityForResult(Intent, int), em que o parâmetro inteiro identifica a chamada.

Esse identificador serve para 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 há atividade filha, ela pode chamar setResult(int) para retornar os 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 valores personalizados a partir de RESULT_FIRST_USER.

Além disso, a atividade filha pode retornar um objeto Intent que contenha todos os dados extras que quiser. A atividade mãe usa o método onActivityResult(int, int, Intent) com o identificador inteiro que ela forneceu originalmente para receber a informação.

Se uma atividade filha falhar por qualquer motivo, como falha, 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, o mesmo app, e uma está iniciando a outra. Esta é a ordem das operações que ocorrem quando a atividade A inicia a atividade B:

  1. O método onPause() da atividade A é executado.
  2. Os métodos onCreate(), onStart() e onResume() da atividade B são executados em sequência A atividade B agora tem o foco no usuário.
  3. Se a atividade A não estiver mais visível na tela, o método onStop() dela será executado.

Essa sequência de callbacks do ciclo de vida permite gerenciar a transição de informações de uma atividade para outra.