The Android Developer Challenge is back! Submit your idea before December 2.

Atividades

Activity é um componente de aplicativo que fornece uma tela com a qual os usuários podem interagir para fazer algo, como discar um número no telefone, tirar uma foto, enviar um e-mail ou ver um mapa. Cada atividade recebe uma janela que exibe a interface do usuário. Geralmente, a janela preenche a tela, mas pode ser menor que a tela e flutuar sobre outras janelas.

Aplicativos geralmente têm várias atividades pouco vinculadas entre si. Normalmente, uma atividade em um aplicativo é especificada como "principal", que é a apresentada ao usuário ao iniciar o aplicativo pela primeira vez. Cada atividade pode, então, iniciar outra atividade para executar diferentes ações. Ao iniciar uma nova atividade, a atividade anterior é interrompida, mas o sistema conserva a atividade em uma pilha (a "pilha de retorno"). Quando uma atividade inicia, ela é enviada para a pilha de retorno e obtém o foco do usuário. A pilha de retorno segue o mecanismo básico de pilha UEPS (o último que entra é o primeiro que sai). Assim, quando o usuário terminar a atividade atual e apertar o botão Voltar, ela sairá da pilha (é destruída) e a atividade anterior será retomada. (a pilha de retorno é discutida em mais detalhes no documento Tarefas e pilha de retorno).

Quando uma atividade é interrompida devido ao início de uma nova atividade, ela é notificada acerca dessa alteração de estado por meio de métodos de retorno de chamada do ciclo de vida da atividade. Há diversos métodos de retorno de chamada que uma atividade pode receber devido a uma alteração em seu estado — quando o sistema a está criando, interrompendo, retomando ou destruindo — e cada retorno de chamada oferece uma oportunidade de executar trabalhos específicos adequados a essa alteração de estado. Por exemplo: quando interrompida, a atividade deve liberar todos os objetos grandes, como conexões com a rede ou com um banco de dados. Quando a atividade for retomada, será possível readquirir os recursos necessários e retomar as ações interrompidas. Essas transições de estado são parte do ciclo de vida da atividade.

O restante deste documento discute o básico sobre a compilação e o uso de uma atividade, incluindo uma discussão completa sobre o funcionamento do ciclo de vida da atividade para gerenciar adequadamente a transição entre os diversos estados da atividade.

Criação de uma atividade

Para criar uma atividade, é preciso criar uma subclasse de Activity (ou uma respectiva subclasse existente). Na subclasse, é preciso implementar um método de retorno de chamada que o sistema chama quando ocorre a transição entre os diversos estados de seu ciclo de vida, como na criação, interrupção, retomada ou destruição da atividade. Os dois métodos mais importantes de retorno de chamada são:

onCreate()
É preciso implementar esse método. O sistema o chama ao criar a atividade. Na implementação, é preciso inicializar os componentes essenciais da atividade. E, fundamentalmente, é quando se deve chamar setContentView() para definir o layout da interface do usuário da atividade.
onPause()
O sistema chama esse método como o primeiro indício de que o usuário está saindo da atividade (embora não seja sempre uma indicação de que a atividade será destruída). É quando geralmente se deve confirmar qualquer alteração que deva persistir além da sessão do usuário atual (porque o usuário pode não retornar).

Há outros métodos de retorno de chamada do ciclo de vida que se pode usar para oferecer uma experiência fluida ao usuário entre atividades e processar interrupções inesperadas que venham a parar ou até a destruir a atividade. Todos os métodos de retorno de chamada do ciclo de vida serão discutidos mais adiante na seção sobre Gerenciamento do ciclo de vida da atividade.

Implementação de uma interface do usuário

A interface do usuário de uma atividade é fornecida por uma hierarquia de objetos — de exibições derivados da classe View. Cada exibição controla um espaço retangular específico dentro da janela da atividade e pode responder à interação com o usuário. Por exemplo: uma exibição pode ser um botão que inicia uma ação quando o usuário toca nele.

O Android oferece algumas exibições prontas que podem ser usadas para projetar e organizar o layout. "Widgets" são exibições que fornecem elementos visuais (e interativos) à tela, como um botão, um campo de texto, uma caixa de seleção ou apenas uma imagem. "Layouts" são exibições derivadas de ViewGroup que oferecem um modelo de layout exclusivo para suas exibições filhas, como um layout linear, um layout em grade ou um layout relativo. Também é possível definir como subclasse as classes View e ViewGroup (ou subclasses existentes) para criar widgets e layouts próprios e aplicá-los no layout da atividade.

A forma mais comum de definir um layout usando exibições é com um arquivo de layout XML salvo nos recursos do aplicativo. Assim, é possível manter o projeto da interface do usuário separado do código-fonte que define o comportamento da atividade. É possível definir o layout como a IU da atividade com setContentView(), passando o ID de recurso do layout. No entanto, também é possível criar novas Views no código da atividade e compilar uma hierarquia de exibições inserindo novas View em um ViewGroup e, em seguida, usar esse layout passando a raiz ViewGroup para setContentView().

Para obter mais informações sobre a criação de uma interface do usuário, consulte a documentação Interface do usuário.

Declaração de uma atividade no manifesto

É preciso declarar a atividade no arquivo de manifesto para torná-la acessível para o sistema. Para declarar a atividade, abra o arquivo de manifesto e adicione um elemento <activity> como filho do elemento <application> . Por exemplo:

<manifest ... >
  <application ... >
      <activity android:name=".ExampleActivity" />
      ...
  </application ... >
  ...
</manifest >

Existem outros atributos que podem ser incluídos nesse elemento para definir propriedades da atividade, como o rótulo, um ícone para ela ou um tema para estilizar sua IU. O atributo android:name é o único necessário — ele especifica o nome da classe da atividade. Depois de publicar o aplicativo, não se deve alterar o nome porque, se isso acontecer, podem ocorrer problemas em algumas funcionalidades, como os atalhos do aplicativo (leia a publicação do blog Coisas que não podem mudar).

Consulte a referência do elemento <activity> para obter mais informações sobre como declarar a atividade no manifesto.

Uso de filtros de intents

Um elemento <activity> também pode especificar vários filtros de intents — usando o elemento <intent-filter> — para declarar o modo com que os componentes de aplicativo podem ativá-lo.

Ao criar um novo aplicativo com as ferramentas do Android SDK, o esboço da atividade criado contém automaticamente um filtro de intent que declara que a atividade responde à ação "main" (principal) e deve ser colocada na categoria "launcher” (inicializador). O filtro de intent tem a seguinte aparência:

<activity android:name=".ExampleActivity" android:icon="@drawable/app_icon">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

O elemento <action> especifica que esse é o “principal” ponto de entrada do aplicativo. O elemento <category> especifica que essa atividade deve ser listada no inicializador do aplicativo do sistema (para permitir que os usuários iniciem essa atividade).

Se o intent é que o aplicativo seja autônomo e que não permita que outros aplicativos ativem suas atividades, não será necessário nenhum outro filtro de intent. Só uma atividade deve ter a ação "main" e a categoria "launcher", como no exemplo anterior. As atividades que não devem estar disponíveis a outros aplicativos não devem ter filtros de intent, já que é possível iniciá-las por meio de intents explícitos (conforme discutido na seção a seguir).

No entanto, se a atividade deve responder a intents implícitos derivadas de outros aplicativos (e do aplicativo em questão), é preciso definir filtros de intents adicionais para a atividade. Para cada tipo de intent a que deseja responder, é preciso incluir um <intent-filter> que contenha um elemento <action> e, opcionalmente, um elemento <category> e/ou um elemento <data>. Esses elementos especificam o tipo de intent a que a atividade pode responder.

Para obter mais informações sobre a forma com que as atividades podem responder a intents, consulte o documento Intents e filtros de intents.

Início de uma atividade

Para iniciar outra atividade, é possível chamar startActivity() passando uma Intent que descreva a atividade que se deseja iniciar. O intent especifica a atividade exata que deve 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 intent também pode portar pequenas quantidades de dados a serem usados pela atividade iniciada.

Ao trabalhar no aplicativo, frequentemente será necessário iniciar uma atividade conhecida. Para isso, pode-se criar um intent que defina explicitamente a atividade que deve ser iniciada por meio de um nome de classe. Por exemplo, eis como uma atividade inicia outra atividade de nome SignInActivity:

Intent intent = new Intent(this, SignInActivity.class);
startActivity(intent);

No entanto, o aplicativo também pode ter que executar algumas ações, como enviar um e-mail, mensagem de texto ou atualização de status usando os dados da atividade em questão. Nesse caso, o aplicativo pode não ter as próprias atividades para executar tais ações; para isso, pode-se aproveitar as atividades fornecidas por outros aplicativos do dispositivo que podem executar essas ações. Esses 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:

Intent intent = new Intent(Intent.ACTION_SEND);
intent.putExtra(Intent.EXTRA_EMAIL, recipientArray);
startActivity(intent);

O EXTRA_EMAIL adicionado ao intent é uma matriz de strings de endereços de e-mail para os quais o e-mail poderá ser enviado. Quando um aplicativo de e-mail responde a esse intent, ele lê a matriz de strings fornecida no extra e coloca-a no campo "para" do formulário de composição do e-mail. Nessa situação, a atividade do aplicativo de e-mail inicia e, quando o usuário termina o trabalho, sua atividade é retomada.

Início de uma atividade para um resultado

Às vezes, é necessário receber um resultado de alguma atividade iniciada. Nesse caso, inicie a atividade chamando startActivityForResult() (em vez de startActivity()). Para receber o resultado de uma atividade subsequente, implemente o método de retorno de chamada onActivityResult(). Quando a atividade subsequente estiver concluída, ela retornará um resultado em uma Intent para o método onActivityResult().

Por exemplo, talvez você queira que o usuário escolha um dos contatos dele, deste modo, a atividade poderá fazer algo com as informações naquele contato. Eis como criar um intent desse tipo e processar o resultado:

private void pickContact() {
    // Create an intent to "pick" a contact, as defined by the content provider URI
    Intent intent = new Intent(Intent.ACTION_PICK, Contacts.CONTENT_URI);
    startActivityForResult(intent, PICK_CONTACT_REQUEST);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    // If the request went well (OK) and the request was PICK_CONTACT_REQUEST
    if (resultCode == Activity.RESULT_OK && requestCode == PICK_CONTACT_REQUEST) {
        // Perform a query to the contact's content provider for the contact's name
        Cursor cursor = getContentResolver().query(data.getData(),
        new String[] {Contacts.DISPLAY_NAME}, null, null, null);
        if (cursor.moveToFirst()) { // True if the cursor is not empty
            int columnIndex = cursor.getColumnIndex(Contacts.DISPLAY_NAME);
            String name = cursor.getString(columnIndex);
            // Do something with the selected contact's name...
        }
    }
}

Esse exemplo mostra a lógica básica que deve ser usada no método onActivityResult() para processar o resultado de uma atividade. A primeira condição verifica se a solicitação foi bem-sucedida — se for, o resultCode será RESULT_OK — e se a solicitação a que esse resultado responderá for desconhecida —, nesse caso, o requestCode corresponderá ao segundo parâmetro com startActivityForResult(). A partir daí, o código processa o resultado da atividade com uma consulta dos dados retornados em uma Intent (o parâmetro data).

Nesse momento, um ContentResolver executa uma consulta em um provedor de conteúdo, que retorna um Cursor que permite a leitura dos dados consultados. Para obter mais informações, consulte o documento Provedores de conteúdo.

Para obter mais informações sobre intents, consulte o documento Intents e filtros de intents.

Encerramento de uma atividade

Para encerrar uma atividade, chame o método finish(). Também é possível encerrar uma atividade separada iniciada anteriormente chamando finishActivity().

Observação: Na maioria dos casos, não se deve finalizar explicitamente uma atividade usando esses métodos. Conforme discutido na seção anterior sobre o ciclo de vida da atividade, o sistema Android gerencia a vida de uma atividade, portanto, não é necessário finalizar as atividades. Chamar esses métodos poderia afetar negativamente a experiência do usuário esperada e isso só deve ser usado quando realmente não se desejar que o usuário retorne a essa instância da atividade.

Gerenciamento do ciclo de vida da atividade

O gerenciamento do ciclo de vida das atividades por meio da implementação de métodos de retorno de chamada é essencial para desenvolver um aplicativo flexível. O ciclo de vida de uma atividade é diretamente afetado pela associação a outras atividades, pela tarefa e pela pilha de retorno.

Uma atividade pode existir essencialmente em três estados:

Resumed
A atividade está em primeiro plano na tela e tem o foco do usuário (em geral, chama-se esse estado de “em execução”).
Paused
A atividade ainda está visível, mas outra atividade está em primeiro plano e tem o foco. Ou seja, outra atividade está visível por cima desta e está parcialmente transparente ou não cobre inteiramente a tela. Uma atividade pausada está totalmente ativa (o objeto Activity está retido na memória, mantém todas as informações de estado e do membro e permanece anexado ao gerenciador de janela), mas pode ser eliminada pelo sistema em situações de memória extremamente baixa.
Stopped
A atividade está totalmente suplantada por outra (a atividade passa para "segundo plano"). Uma atividade interrompida ainda está ativa (o objeto Activity está retido na memória, mantém todas as informações de estado e do membro, mas não está anexado ao gerenciador de janelas). No entanto, ela não fica mais visível para o usuário e pode ser eliminada pelo sistema se a memória for necessária em outro processo.

Se uma atividade estiver pausada ou interrompida, o sistema poderá descartá-la da memória solicitando a finalização do processo (chamando o método finish()) ou simplesmente eliminando-o. Quando a atividade for reaberta (depois de finalizada ou eliminada), ele deverá ser totalmente recriada.

Implementação dos retornos de chamada do ciclo de vida

Quando uma atividade transita entre os diferentes estados descritos acima, ela é notificada por meio de vários métodos de retorno de chamada. Todos os métodos de retorno de chamada são ganchos que podem ser modificados para executar um trabalho adequado quando o estado da atividade muda. O esqueleto de atividade a seguir contém cada um dos métodos do ciclo de vida fundamentais:

public class ExampleActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // The activity is being created.
    }
    @Override
    protected void onStart() {
        super.onStart();
        // The activity is about to become visible.
    }
    @Override
    protected void onResume() {
        super.onResume();
        // The activity has become visible (it is now "resumed").
    }
    @Override
    protected void onPause() {
        super.onPause();
        // Another activity is taking focus (this activity is about to be "paused").
    }
    @Override
    protected void onStop() {
        super.onStop();
        // The activity is no longer visible (it is now "stopped")
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        // The activity is about to be destroyed.
    }
}

Observação: A implementação desses métodos do ciclo de vida deve sempre chamar a implementação da superclasse antes de realizar qualquer trabalho, conforme ilustrado no exemplo acima.

Juntos, esses métodos definem todo o ciclo de vida da atividade. Ao implementá-los, é possível monitorar três loops aninhados no ciclo de vida da atividade:

  • Todo o tempo de vida de uma atividade acontece entre a chamada de onCreate() e a chamada de onDestroy(). A atividade deve executar a configuração de estado "global" (como definir layout) em onCreate() e liberar todos os recursos restantes em onDestroy(). Por exemplo: se a atividade tiver um encadeamento em execução em segundo plano para baixar dados da rede, ela poderá criá-lo em onCreate() e, em seguida, interrompê-lo em onDestroy().
  • O tempo de vida visível de uma atividade acontece entre a chamada de onStart() e a chamada de onStop(). Durante esse tempo, o usuário pode ver a atividade na tela e interagir com ela. Por exemplo, onStop() é chamado quando uma nova atividade inicia e ela não fica mais visível. Entre esses dois métodos, é possível manter os recursos necessários para exibir a atividade ao usuário. Por exemplo: você poderá registrar um BroadcastReceiver em onStart() para monitorar as alterações que afetem a IU e cancelar o registro em onStop() quando o usuário não puder mais ver o que você está exibindo. O sistema pode chamar onStart() e onStop() várias vezes durante todo o tempo de vida de uma atividade enquanto ela alterna entre visível e oculta ao usuário.

  • O tempo de vida em primeiro plano de uma atividade ocorre entre a chamada de onResume() e a chamada de onPause(). Durante esse tempo, a atividade está na frente de todas as outras atividades na tela e tem o foco de interação do usuário. Frequentemente, uma atividade pode fazer a transição entre o primeiro e o segundo plano — por exemplo, onPause() é chamado quando o dispositivo está em suspensão ou quando uma caixa de diálogo é exibida. Como esse estado pode fazer transições frequentemente, o código nesses dois métodos deve ser bem leve para evitar transições lentas que façam o usuário esperar.

A figura 1 ilustra esses loops e os caminhos que uma atividade pode tomar entre os estados. Os retângulos representam os métodos de retorno de chamada que podem ser implementados para executar operações quando a atividade faz a transição entre estados.

Figura 1. Ciclo de vida da atividade.

Os mesmos métodos de retorno de chamada do ciclo de vida são listados na tabela 1, que descreve cada um deles em mais detalhes e localiza cada um dentro do ciclo de vida geral da atividade, inclusive se o sistema pode eliminar a atividade depois da conclusão do método de retorno de chamada.

Tabela 1. Resumo dos métodos de retorno de chamada do ciclo de vida da atividade.

Método Descrição Pode ser eliminado depois? Próximo
onCreate() Chamado quando a atividade é criada pela primeira vez. É onde se deve fazer toda a configuração estática normal — criar exibições, vincular dados a listas etc. Esse método recebe um objeto Bundle (pacote) contendo o estado anterior da atividade, caso esse estado tenha sido capturado (consulte Gravação do estado da atividade mais adiante).

Sempre seguido de onStart().

Não onStart()
     onRestart() Chamado depois que atividade tiver sido interrompida, logo antes de ser reiniciada.

Sempre seguido de onStart().

Não onStart()
onStart() Chamado logo antes de a atividade se tornar visível ao usuário.

Seguido de onResume() se a atividade for para segundo plano ou onStop() se ficar oculta.

Não onResume()
ou
onStop()
     onResume() Chamado logo antes de a atividade iniciar a interação com o usuário. Nesse ponto, a atividade estará no topo da pilha de atividades com a entrada do usuário direcionada a ela.

Sempre seguido de onPause().

Não onPause()
onPause() Chamado quando o sistema está prestes a retomar outra atividade. Esse método normalmente é usado para confirmar alterações não salvas a dados persistentes, animações interrompidas e outras coisas que talvez estejam consumindo CPU e assim por diante. Ele sempre deve fazer tudo bem rapidamente porque a próxima atividade não será retomada até ela retornar.

Seguido de onResume() se a atividade retornar para a frente ou de onStop() se ficar invisível ao usuário.

Sim onResume()
ou
onStop()
onStop() Chamado quando a atividade não está mais visível ao usuário. Isso pode acontecer porque ela está sendo destruída ou porque outra atividade (uma existente ou uma nova) foi retomada e está cobrindo-a.

Seguido de onRestart() se a atividade estiver voltando a interagir com o usuário ou onDestroy() se estiver saindo.

Sim onRestart()
ou
onDestroy()
onDestroy() Chamado antes de a atividade ser destruída. É a última chamada que a atividade receberá. Pode ser chamado porque a atividade está finalizando (alguém chamou finish() nela) ou porque o sistema está destruindo temporariamente essa instância da atividade para poupar espaço. É possível distinguir entre essas duas situações com o método isFinishing(). Sim nada

A coluna de nome “Pode ser eliminado depois?” indica se o sistema pode ou não eliminar o processo que hospeda a atividade a qualquer momento após o método retornar sem executar outra linha de código da atividade. Estes três métodos são marcados como "sim": ({@linkandroid.app.Activity#onPause onPause()}, onStop() e onDestroy()). Como onPause() é o primeiro dos três, assim que a atividade é criada, onPause() é o último método que certamente será chamado antes que o processo possa ser eliminado — se o sistema precisar recuperar memória em uma emergência, onStop() e onDestroy() poderão não ser chamados. Portanto, deve-se usar onPause() para gravar dados persistentes cruciais (como edições do usuário) no armazenamento. No entanto, deve-se sempre ser seletivo acerca das informações que devem ser retidas durante onPause() porque qualquer procedimento de bloqueio nesse método bloqueará a transição para a próxima atividade e retardará a experiência do usuário.

Os métodos marcados como "Não" na coluna Pode ser eliminado depois? protegem o processo que hospeda a atividade, evitando a eliminação dele no momento em que é chamado. Assim, uma atividade é eliminável do momento em que onPause() retorna ao momento em que onResume() é chamado. Ela não poderá ser eliminada novamente até que onPause() seja chamado de novo e retorne.

Observação: Uma atividade tecnicamente não "eliminável”, por essa definição na tabela 1, ainda pode ser eliminada pelo sistema — mas isso só ocorreria em circunstâncias extremas, quando não houvesse outra opção. A possibilidade de uma atividade ser eliminada é discutida em mais detalhes no documento Processos e encadeamentos.

Gravação do estado da atividade

A introdução ao Gerenciamento do ciclo de vida da atividade menciona brevemente que, quando uma atividade é pausada ou interrompida, o estado da atividade é retido. Isso acontece porque o objeto Activity continua na memória quando a atividade está pausada ou interrompida — todas as informações sobre os membros e o estado atual ainda estão ativas. Assim, todas as alterações feitas pelo usuário dentro da atividade são retidas, de forma que, quando a atividade retornar ao primeiro plano (quando é "retomada"), essas alterações ainda estejam lá.

No entanto, quando o sistema destrói uma atividade para recuperar memória, o objeto Activity é destruído, por isso o sistema não pode simplesmente retomá-la com o estado intacto. Em vez disso, o sistema tem que recriar o objeto Activity se o usuário navegar de volta a ele. Ainda assim, o usuário não estará ciente de que o sistema destruiu a atividade e a recriou e, assim, provavelmente esperará que a atividade esteja exatamente como antes. Nessa situação, para garantir que as informações importantes sobre o estado da atividade sejam preservadas, implementa-se um método adicional de retorno de chamada que permite salvar as informações sobre o estado da atividade: onSaveInstanceState().

O sistema chama onSaveInstanceState() antes de deixar a mensagem vulnerável à destruição. O sistema passa a esse método um Bundle, no qual é possível salvar informações de estado acerca da atividade, como pares de nome-valor, usando métodos como putString() e putInt(). Em seguida, se o sistema eliminar o processo do aplicativo e o usuário voltar à atividade, o sistema recriará a atividade e passará o Bundle a onCreate() e a onRestoreInstanceState(). Usando qualquer um desses métodos, é possível extrair o estado salvo de Bundle e restaurar o estado da atividade. Se não houver informações de estado a restaurar, o Bundle passado será nulo (que é o caso quando a atividade é criada pela primeira vez).

Figura 2. As duas formas pelas quais uma atividade retorna ao foco do usuário com o estado intacto: ou a atividade é destruída e recriada em seguida — e ela deve restaurar o estado salvo anteriormente — ou a atividade é interrompida e retomada em seguida — e o estado dela permanece intacto.

Observação: Não há garantia nenhuma de que onSaveInstanceState() será chamado antes de a atividade ser destruída porque há casos em que não será necessário salvar o estado (como quando o usuário sai da atividade usando o botão Voltar) porque o usuário está fechando explicitamente a atividade). Se o sistema chamar onSaveInstanceState(), ele o fará antes de onStop() e possivelmente antes de onPause().

No entanto, mesmo se você não fizer nada e não implementar onSaveInstanceState(), parte do estado da atividade será restaurada pela implementação padrão da classe Activity de onSaveInstanceState(). Especificamente, a implementação padrão chama o método onSaveInstanceState() correspondente para cada View no layout, o que permite que cada exibição forneça informações próprias sobre o que deve ser salvo. Quase todo widget na estrutura do Android implementa esse método conforme o necessário, de forma que qualquer alteração visível na IU seja automaticamente salva e restaurada ao criar a atividade. Por exemplo, o widget EditText salva qualquer texto inserido pelo usuário e o widget CheckBox salva independente de estar marcado. O único trabalho necessário será fornecer um ID exclusivo (com o atributo android:id) para cada widget que for salvar seu estado. Se o widget não salvar nenhum ID, o sistema não poderá salvar seu estado.

Embora a implementação padrão de onSaveInstanceState() salve informações úteis sobre a IU da atividade, talvez ainda seja necessário modificá-la para salvar informações adicionais. Por exemplo: pode ser necessário salvar valores de membro alterados durante a vida da atividade (possivelmente correlacionados a valores restaurados na IU, mas os membros que retêm esses valores de IU, por padrão, não são restaurados).

Como a implementação padrão de onSaveInstanceState() ajuda a salvar o estado da IU, se o método for modificado para salvar informações de estado adicionais, deve-se sempre chamar a implementação da superclasse de onSaveInstanceState() antes de fazer qualquer trabalho. Da mesma forma, deve-se também chamar a implementação da superclasse de onRestoreInstanceState() se ela for modificada para que a implementação padrão possa restaurar estados da exibição.

Observação: Como nem sempre onSaveInstanceState() é chamado, deve-se usá-lo somente para registrar o estado temporário da atividade (o estado da IU) — nunca se deve usá-lo para armazenar dados persistentes. Em vez disso, deve-se usar onPause() para armazenar dados persistentes (como dados que devem ser salvos em um banco de dados) quando o usuário sair da atividade.

Uma boa forma de testar a capacidade do aplicativo de restaurar seu estado é girar o dispositivo para alterar a orientação da tela. Quando a orientação de tela muda, o sistema destrói e recria a atividade para aplicar recursos alternativos que podem ser disponibilizados para a nova configuração de tela. Por esse motivo somente, é muito importante que a atividade restaure completamente seu estado quando for recriada porque os usuários normalmente giram a tela ao usar aplicativos.

Processamento de alterações de configuração

Algumas configurações do dispositivo podem mudar em tempo de execução (como a orientação da tela, disponibilidade do teclado e idioma). Quando ocorre uma alteração, o Android recria a atividade em execução (o sistema chama onDestroy() e, em seguida, chama onCreate() imediatamente). Esse comportamento foi projetado para ajudar o aplicativo a se adaptar a novas configurações recarregando-o automaticamente com recursos alternativos fornecidos pelo programador (como diferentes layouts para orientações e tamanhos de telas diferentes).

Se você projetar adequadamente a atividade para processar um reinício devido a uma alteração na orientação da tela e restaurar o estado da atividade conforme descrito acima, o aplicativo será mais resiliente a outros eventos inesperados no ciclo de vida da atividade.

A melhor forma de processar um reinício desse tipo é salvar e restaurar o estado da atividade com onSaveInstanceState() e onRestoreInstanceState() (ou com onCreate(), conforme abordado na seção anterior.

Para obter mais informações sobre alterações de configuração que podem ocorrer em tempo de execução e como tratá-las, leia o guia em Processamento de alterações em tempo de execução.

Coordenação de atividades

Quando uma atividade inicia outra, ambas passam por transições no ciclo de vida. A primeira atividade é pausada e interrompida (embora ela não seja interrompida se ainda estiver visível em segundo plano) 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 retornos de chamada do ciclo de vida é bem definida, especialmente quando as duas atividades estão no mesmo processo e uma está iniciando a outra. Essa é 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, seu método onStop() será executado.

Essa sequência previsível de retornos de chamada do ciclo de vida permite gerenciar a transição de informações de uma atividade para outra. Por exemplo, se você for gravar em um banco de dados o momento em que a primeira atividade é interrompida para que a atividade a seguir possa lê-lo, será preciso realizar a gravação no banco de dados durante onPause() e não durante onStop().