Entenda o 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 uma quantidade de callbacks que permite que a atividade saiba sobre a mudança do estado: informa a respeito da criação, interrupção ou retomada de uma atividade ou da destruição do processo em que ela reside por parte do sistema.

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 reprodutor de streaming de vídeo, poderá pausar o vídeo e encerrar a conexão de rede quando o usuário alternar para outro aplicativo. Quando o usuário retornar, será possível reconectar à rede e permitir que ele retome o vídeo de onde parou. Ou seja, 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 a garantir que seu aplicativo evite os problemas a seguir:

  • 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, é explicado cada um dos callbacks: o que acontece internamente quando eles operam e o que você deve 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, inclusive orientação sobre práticas recomendadas, veja Como gerenciar componentes cientes do ciclo de vida e Como salvar estados da IU. Para saber como arquitetar um aplicativo robusto e de qualidade de produção usando atividades em combinação com os componentes da arquitetura, consulte Guia para a arquitetura de aplicativos.

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(). Conforme a atividade entra em um novo estado, o sistema invoca cada um desses callbacks.

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

Imagem 1. 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, essa eliminação é somente parcial. A atividade ainda reside na memória (como quando o usuário alterna para outro aplicativo) e ainda pode voltar ao primeiro plano. Caso o usuário retorne à atividade, ela é retomada de onde havia sido interrompida. Com algumas exceções, os aplicativos são impedidos de iniciar atividades quando operam em segundo plano.

A probabilidade de o sistema eliminar um determinado processo, juntamente com as atividades nele, depende do estado da atividade no momento. Em Estado da atividade e ejeção da memória, há mais informações sobre o relacionamento entre o estado e a vulnerabilidade para ejeção.

Dependendo da complexidade de sua atividade, não há necessidade de implementar todos os métodos do ciclo de vida. No entanto, é importante compreender cada um deles e implementar somente os que garantem que o aplicativo tenha o desempenho esperado pelo usuário.

A próxima seção deste documento fornece detalhes sobre os callbacks usados para processar as transições entre os estados.

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, como chamar setContentView(), fazem parte dos métodos do ciclo de vida da atividade em si. No entanto, o código que implementa as ações de um componente dependente deve ser colocado no próprio componente. Para fazer isso, você precisa tornar o componente dependente ciente do ciclo de vida. Veja Como gerenciar componentes cientes do ciclo de vida para saber como tornar seus componentes dependentes cientes do ciclo de vida.

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(), você executa a lógica básica de inicialização do aplicativo. Isso deve acontecer 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, um objeto Bundle que contém o estado anteriormente salvo da atividade. Se a atividade nunca existiu, o valor do objeto Bundle será nulo.

Caso você tenha um componente ciente do ciclo de vida conectado ao ciclo de vida da sua atividade, ele receberá o evento ON_CREATE. O método anotado com @OnLifecycleEvent será 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 seguinte exemplo 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 é especificado passando o código 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 super class onCreate to complete the creation of activity like
    // the view hierarchy
    super.onCreate(savedInstanceState)

    // recovering the instance state
    gameState = savedInstanceState?.getString(GAME_STATE_KEY)

    // set the user interface layout for this activity
    // the layout file is defined in the project res/layout/main_activity.xml file
    setContentView(R.layout.main_activity)

    // initialize member TextView so we can manipulate it later
    textView = findViewById(R.id.text_view)
}

// This callback is called only when there is a saved instance that is previously saved by using
// onSaveInstanceState(). We restore some state in onCreate(), while we can optionally restore
// other state 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 may 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 super class onCreate to complete the creation of activity like
    // the view hierarchy
    super.onCreate(savedInstanceState);

    // recovering the instance state
    if (savedInstanceState != null) {
        gameState = savedInstanceState.getString(GAME_STATE_KEY);
    }

    // set the user interface layout for this activity
    // the layout file is defined in the project res/layout/main_activity.xml file
    setContentView(R.layout.main_activity);

    // initialize member TextView so we can manipulate it later
    textView = (TextView) findViewById(R.id.text_view);
}

// This callback is called only when there is a saved instance that is previously saved by using
// onSaveInstanceState(). We restore some state in onCreate(), while we can optionally restore
// other state 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 may 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 por meio da inserção de novas Views em um ViewGroup. Em seguida, você usará o layout passando a raiz ViewGroup para setContentView(). Para ter 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 reside no estado Criado. Após o método onCreate() concluir a execução, a atividade insere o estado Iniciado e o sistema chama os métodos onStart() e onResume() em rápida sucessão. A próxima seção explica o callback onStart().

onStart()

Quando a atividade insere o estado Iniciado, o sistema invoca esse callback. A chamada onStart() torna a atividade visível ao usuário, à medida que o aplicativo prepara a atividade para inserir o primeiro plano e se tornar interativa. Por exemplo, é nesse método que o aplicativo inicializa o código que mantém a IU.

Quando a atividade é movida para o estado Iniciado, qualquer componente ciente do ciclo de vida que esteja ligado ao ciclo de vida da atividade receberá o evento ON_START.

O método onStart() faz a conclusão muito rapidamente e, como no caso do estado Criado, a atividade não reside 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 insere o 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 aplicativo é mantido nesse estado até que algo ocorra e retire o foco do aplicativo. Um evento assim pode ser, por exemplo, o recebimento de uma chamada telefônica, a navegação do usuário para outra atividade ou o desligamento da tela do dispositivo.

Quando a atividade é movida para o estado Retomado, qualquer componente ciente do ciclo de vida ligado ao ciclo de vida da atividade receberá 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 insere o estado Pausado e o sistema invoca o callback onPause().

Caso a atividade retorne do estado Pausado para o estado Retomado, o sistema chamará novamente o método onResume(). Dessa forma, implemente o onResume() para inicializar os componentes liberados durante onPause() e execute 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 acima 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 usuário está no modo de várias janelas e toca na outra janela que não contém a atividade, essa atividade será movida para o estado Pausado. Se você quiser que a câmera seja ativada somente quando o aplicativo estiver Retomado (visível e ativo em primeiro plano), inicialize a câmera após o evento ON_RESUME demonstrado acima. Caso queira mantê-la ativada quando a atividade estiver Pausada embora visível (por exemplo, no modo de várias janelas), inicialize a câmera após o evento ON_START. Perceba, no entanto, que 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 pode ser necessário manter a câmera ativa enquanto a atividade estiver Pausada, mas, caso isso seja feito, a experiência geral do usuário pode ser prejudicada. Pense com cuidado sobre o local do ciclo de vida onde é mais apropriado assumir o controle dos recursos compartilhados do sistema no contexto 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 finalize esse item após o evento ON_STOP. Caso você inicialize após o evento ON_RESUME, faça a liberação após o evento ON_PAUSE.

O snippet de código acima posiciona 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. A adição dessa 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. Consulte Como gerenciar componentes cientes do ciclo de vida para saber como criar um componente ciente do ciclo de vida.

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 (embora ainda possa estar visível se o usuário estiver no modo de várias janelas). Use o método onPause() para pausar ou ajustar operações que não devem continuar (ou que precisem continuar com moderação) enquanto a Activity estiver no modo Pausado e aquelas que você espera retomar em breve. Há vários motivos pelos quais uma atividade pode entrar nesse estado. Por exemplo:

  • Algum evento interromper a execução do aplicativo, conforme descrito na seção onResume(). Esse é o caso mais comum.
  • No Android 7.0 (API de nível 24) ou posteriores, diversos aplicativos operam no modo de várias janelas. Como só um dos aplicativos (janelas) tem foco a qualquer momento, o sistema pausa todos os outros aplicativos.
  • Uma nova atividade semitransparente (como uma caixa de diálogo) é aberta. Enquanto a atividade estiver parcialmente visível, mas não for a atividade em foco, ela permanecerá pausada.

Quando a atividade é movida para o estado pausado, qualquer componente ciente do ciclo de vida ligado ao ciclo de vida da atividade receberá 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.

Também é possível usar o método onPause() para liberar recursos do sistema, tratamento de sensores (como GPS) ou quaisquer recursos que possam afetar a duração da bateria enquanto a atividade estiver pausada e o usuário não precisar deles. No entanto, como mencionado acima, na seção onResume(), uma atividade Pausada ainda poderá ser completamente visível no modo de várias janelas. Assim, considere usar onStop() em vez de onPause() para liberar ou ajustar completamente operações e recursos relacionados à IU para melhorar o suporte do modo de várias janelas.

O exemplo a seguir da reação de um LifecycleObserver ao evento ON_PAUSE é a contraparte do evento ON_RESUME acima, que libera a câmera 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;
        }
    }

    ...
}

O snippet de código acima posiciona o código de liberação da câmera após o recebimento do evento ON_PAUSE pelo LifecycleObserver. Como mencionado antes, consulte Como gerenciar componentes cientes do ciclo de vida para saber como criar um componente ciente do ciclo de vida.

A execução 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 do banco de dados. Esse tipo de trabalho pode não ser concluído antes da finalização do método. Em vez disso, realize operações de desligamento pesadas durante onStop(). Para mais informações sobre as operações adequadas a se realizar durante onStop(), consulte onStop(). Para mais informações sobre o salvamento de dados, consulte Como salvar e restaurar o estado da atividade.

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(). Caso a atividade retorne 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(). A próxima seção discutirá o callback onStop().

onStop()

Quando a atividade não estiver mais visível ao usuário, ela inserirá o estado Interrompido e o sistema invocará o callback onStop(). Isso pode ocorrer, por exemplo, quando uma atividade recém-iniciada preenche toda a tela. O sistema também poderá chamar onStop() quando a atividade parar de operar e estiver 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 receberá 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(), o aplicativo liberará ou ajustará recursos desnecessários enquanto o aplicativo não estiver visível ao 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() garante que o trabalho relacionado à IU continue, mesmo quando o usuário estiver 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 mais oportuno 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.
    );
}

A amostra de código acima usa o SQLite diretamente. Em vez disso, use 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 aplicativo, consulte o guia Biblioteca de persistência Room.

Quando a atividade insere o estado Interrompido, o objeto Activity é mantido residente na memória: Ele mantém todas as informações de associação e de estado, mas não é anexado ao gerenciador da janela. 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.

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

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(). A próxima seção explica o callback onDestroy().

onDestroy()

onDestroy() é chamado antes de a atividade ser destruída. O sistema invoca esse callback porque:

  1. a atividade está sendo finalizada (pelo fato de o usuário descartá-la completamente ou devido a finish() ser chamado na atividade); ou
  2. o sistema está destruindo temporariamente a atividade devido a uma mudança na configuração (como a rotação do dispositivo ou o 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 receberá o evento ON_DESTROY. É nesse momento que os componentes do ciclo de vida podem limpar qualquer item que eles precisarem antes da destruição da atividade.

Em vez de inserir lógica à sua atividade para determinar por que ela está sendo destruída, use um objeto ViewModel para manter os dados de visualização relevante a ela. Se a atividade for recriada devido a uma mudança na configuração, o ViewModel não precisará realizar nenhuma ação. Ela será preservada e fornecida à próxima instância da atividade. Se a atividade não for recriada, o ViewModel chamará o método onCleared(), em que ele 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 o resultado da 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.

Os callbacks onDestroy() liberarão todos os recursos ainda não liberados pelos 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 eliminar um determinado processo depende do estado do processo no momento. O estado do processo, por sua vez, depende do estado da atividade em execução no processo. A tabela 1 mostra a correlação entre o estado do processo, o estado da atividade e a probabilidade de o sistema eliminar o processo.

Probabilidade de eliminação Estado do processo Estado da atividade
Mínimo Em primeiro plano (com foco ou prestes a ter) Criado
Iniciado
Retomado
Mais Segundo plano (perde o foco) Pausado
Máximo Segundo plano (não visível) Interrompido
Vazio Destruído

Tabela 1. Relação entre o ciclo de vida do processo e o 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 opera, destruindo não só a atividade, mas também todo o restante em execução no processo. Para saber como preservar e restaurar os estados da IU da atividade quando ocorre o processo de eliminação iniciado pelo sistema, consulte Como salvar e restaurar o estado da atividade.

Um usuário também pode eliminar um processo usando o Gerenciador de aplicativos em “Configurações” para eliminar o aplicativo correspondente.

Para mais informações sobre o processo em geral, consulte Processos e threads. Para saber mais sobre como o ciclo de vida de um processo está ligado aos estados das atividades nele, consulte a seção Ciclo de vida dos processos nessa página.

Como salvar e restaurar o estado transitório da IU

Um usuário espera que o estado da IU de uma atividade permaneça o mesmo durante uma alteração na configuração, como rotação ou mudança 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 estiver ausente e a atividade será interrompida.

Quando a atividade é destruída devido a limitações do sistema, use uma combinação de ViewModel, onSaveInstanceState() e/ou armazenamento local para preservar o estado transitório da IU 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 IU nas atividades iniciais pelo sistema e eliminação do processo, consulte Como salvar o estado da IU.

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 IU forem simples e leves, como um tipo de dados primitivo ou um objeto simples (como String), você poderá usar o onSaveInstanceState() sozinho para manter o estado da IU nas mudanças de configuração e na eliminação do processo iniciada pelo sistema. Na maioria dos casos, entretanto, recomendamos o uso do ViewModel e do onSaveInstanceState() (como destacado em Como salvar o estado da IU), uma vez que o onSaveInstanceState() incorre em custos de serialização/desserializaçã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 pressionou o botão Voltar ou a atividade em si é 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 usados pelo sistema para restaurar o estado anterior são chamados de estados de instância e são uma coleção de pares de chave-valor armazenados no 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 atividade, cada uma delas precisa ter um código exclusivo, fornecido pelo atributo android:id.

Um objeto Bundle não é adequado para preservar mais do que uma quantidade trivial de dados, pois 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 Como salvar estados da IU.

Salve 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 informações sobre o estado da instância adicional para a atividade, você precisa modificar onSaveInstanceState() e adicionar pares de chave-valor ao objeto Bundle salvo no evento em que a atividade é inesperadamente destruída. Se você modificar onSaveInstanceState(), precisará chamar a implementação de superclasse, caso queira que a implementação padrão salve o estado da hierarquia de visualização. Por 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 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 essas oportunidades não surgirem, salve os dados 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 pacote do 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.

Por exemplo, o snippet de código a seguir mostra como restaurar alguns dados do 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 escolher implementar onRestoreInstanceState(), que o sistema chama após o método onStart(). O sistema chama onRestoreInstanceState() se houver um estado salvo a 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);
}

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

Como navegar entre as atividades

É provável que um aplicativo entre e saia de uma atividade, talvez muitas vezes, durante a vida útil do aplicativo. Por exemplo, o usuário pode tocar no botão Voltar do dispositivo ou a atividade pode precisar iniciar 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 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 (e 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. Estes são os casos em que os intents são muito importantes: É possível criar um intent que descreva uma ação a executar para que o sistema inicie a atividade apropriada de outro aplicativo. Se houver mais de uma atividade que possa processar o 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 o 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 ao intent é uma matriz de strings de endereços de e-mail a que o e-mail será enviado. Quando um aplicativo de e-mails responde a esse intent, ele lê a matriz de strings fornecida no extra e a coloca no campo “para” do formulário de composição do e-mail. Nessa situação, a atividade do aplicativo de e-mails inicia 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 desambiguar 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 aplicativos ou atividades. O resultado é retornado por meio do 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 sempre 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), juntamente 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 pai 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) {
                    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.  Here we will just 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 (aplicativo) 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 do usuário).
  3. Em seguida, se a atividade A não estiver mais visível na tela, o método onStop() será executado.

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