Tarefas e a backstack

Uma tarefa é uma coleção de atividades com que os usuários interagem ao tentar fazer algo no app. Essas atividades são organizadas em uma pilha chamada backstack, na ordem em que cada atividade é aberta.

Por exemplo, um app de e-mails pode ter uma atividade para mostrar uma lista de novas mensagens. Quando o usuário seleciona uma mensagem, uma nova atividade é aberta para ver essa mensagem. Essa nova atividade é adicionada à backstack. Em seguida, quando o usuário toca ou gesticula em "Voltar", essa nova atividade termina e é retirada da pilha.

Ciclo de vida de uma tarefa e a respectiva backstack

A tela inicial do dispositivo é o ponto de partida para a maioria das tarefas. Quando um usuário toca no ícone de um app ou atalho na tela de início ou na tela inicial, a tarefa desse app aparece em primeiro plano. Se não houver nenhuma tarefa para o app, uma nova tarefa será criada e a atividade principal do app será aberta como a atividade raiz na pilha.

Quando a atividade atual inicia outra, a nova atividade é empurrada para a parte superior da pilha e recebe o foco. A atividade anterior permanece na pilha, mas é interrompida. Quando uma atividade é interrompida, o sistema mantém o estado atual da interface do usuário. Quando o usuário executa a ação de retorno, a atividade atual é retirada do topo da pilha e destruída. A atividade anterior será retomada, e o estado anterior da interface será restaurado.

As atividades na pilha nunca são reorganizadas, só são enviadas e exibidas da pilha quando são iniciadas pela atividade atual e dispensadas pelo usuário com o botão ou o gesto "Voltar". Portanto, a backstack opera como uma estrutura de objeto último a chegar, primeiro a sair. A Figura 1 mostra uma linha do tempo com as atividades sendo enviadas e retiradas de uma backstack.

Figura 1. Uma representação de como cada nova atividade em uma tarefa adiciona um item à pilha de retorno. Quando o usuário toca ou gesticula "Voltar", a atividade atual é destruída e a atividade anterior é retomada.

À medida que o usuário continua a tocar ou gesticular "Voltar", cada atividade na pilha é retirada para revelar a anterior, até que o usuário retorne à tela inicial ou a qualquer atividade que estivesse em execução no início da tarefa. Quando todas as atividades são removidas da pilha, a tarefa não existe mais.

Comportamento de toque com o botão "Voltar" para atividades raiz da tela de início

As atividades raiz da tela de início são atividades que declaram um filtro de intent com ACTION_MAIN e CATEGORY_LAUNCHER. Essas atividades são únicas porque atuam como pontos de entrada no app pela tela de início e são usadas para iniciar uma tarefa.

Quando um usuário toca ou gesticula em "Voltar" em uma atividade raiz da tela de início, o sistema processa o evento de forma diferente, dependendo da versão do Android que o dispositivo está executando.

Comportamento do sistema no Android 11 e versões anteriores
O sistema encerra a atividade.
Comportamento do sistema no Android 12 e versões mais recentes

O sistema move a atividade e a tarefa para o segundo plano em vez de concluir a atividade. Esse comportamento corresponde ao comportamento padrão do sistema ao sair de um app usando o botão home ou o gesto.

Na maioria dos casos, esse comportamento significa que os usuários podem retomar o app mais rapidamente de um estado quente, em vez de ter que reiniciá-lo completamente a partir de um estado frio.

Se você precisar oferecer navegação de retorno personalizada, recomendamos usar as APIs Activity do AndroidX em vez de substituir onBackPressed(). As APIs Activity do AndroidX consideram automaticamente o comportamento adequado do sistema se não houver componentes que interceptem o toque de retorno do sistema.

No entanto, se o app substituir onBackPressed() para processar a navegação de retorno e concluir a atividade, atualize a implementação para chamar super.onBackPressed() em vez de finalizar. Chamar super.onBackPressed() move a atividade e a tarefa para o segundo plano quando adequado e oferece uma experiência de navegação mais consistente para os usuários em todos os apps.

Tarefas em primeiro e segundo plano

Figura 2. Duas tarefas: a Tarefa B recebe a interação do usuário em primeiro plano, enquanto a Tarefa A está em segundo plano, aguardando para ser retomada.

Uma tarefa é uma unidade coesa que pode ser movida para o segundo plano quando um usuário inicia uma nova tarefa ou acessa a tela inicial. Em segundo plano, todas as atividades da tarefa são interrompidas, mas a backstack da tarefa permanece intacta: a tarefa perde o foco enquanto outra tarefa ocorre, conforme mostrado na Figura 2. Uma tarefa pode retornar ao primeiro plano para que os usuários possam continuar de onde pararam.

Considere o fluxo de tarefas a seguir para a tarefa A atual que tem três atividades na pilha, incluindo duas na atividade atual:

  1. O usuário usa o botão ou o gesto "Página inicial" e inicia um novo app na tela de início.

    Quando a tela inicial aparecer, a Tarefa A vai para o segundo plano. Quando o novo app é iniciado, o sistema inicia uma tarefa para ele (Tarefa B) com a própria pilha de atividades.

  2. Depois de interagir com esse app, o usuário retorna à tela inicial novamente e seleciona o app que iniciou a Tarefa A originalmente.

    Agora, a Tarefa A passa para o primeiro plano. Todas as três atividades na pilha estão intactas, e a atividade na parte de cima da pilha é retomada. Nesse momento, o usuário também pode voltar para a Tarefa B acessando a tela inicial e selecionando o ícone do app que iniciou essa tarefa ou selecionando a tarefa do app na tela Recentes.

Várias instâncias de atividade

Figura 3. Uma única atividade pode ser instanciada várias vezes.

Como as atividades na backstack nunca são reorganizadas, se o app permitir que os usuários iniciem uma atividade específica em mais de uma atividade, uma nova instância dessa atividade vai ser criada e enviada para a pilha, em vez de trazer qualquer instância anterior da atividade para o topo. Dessa forma, uma atividade no app pode ser instanciada várias vezes, mesmo de tarefas diferentes, como mostrado na Figura 3.

Se o usuário navegar para trás usando o botão ou o gesto de voltar, as instâncias da atividade serão reveladas na ordem em que foram abertas, cada uma com o próprio estado de interface. No entanto, você pode modificar esse comportamento se não quiser que uma atividade seja instanciada mais de uma vez. Saiba mais sobre isso na seção sobre como gerenciar tarefas.

Ambientes com várias janelas

Quando os apps são executados simultaneamente em um ambiente de várias janelas, com suporte no Android 7.0 (nível 24 da API) e versões mais recentes, o sistema gerencia as tarefas separadamente para cada janela. Cada janela pode ter várias tarefas. O mesmo vale para apps Android em execução em Chromebooks: o sistema gerencia tarefas, ou grupos de tarefas, por janela.

Resumo do ciclo de vida

Para resumir o comportamento padrão de atividades e tarefas:

  • Quando a atividade A inicia a atividade B, ela é interrompida, mas o sistema mantém o estado, como a posição de rolagem e qualquer texto inserido em formulários. Se o usuário tocar ou usar o gesto "Voltar" durante a atividade B, a atividade A será retomada com o estado restaurado.

  • Quando o usuário sai de uma tarefa usando o botão home ou o gesto, a atividade atual é interrompida, e a tarefa fica em segundo plano. O sistema retém o estado de todas as atividades na tarefa. Se o usuário retomar a tarefa mais tarde selecionando o ícone na tela de início que a iniciou, ela vai voltar para o primeiro plano e retomar a atividade na parte de cima da pilha.

  • Se o usuário tocar ou gesticular "Voltar", a atividade atual será retirada da pilha e destruída. A atividade anterior na pilha será retomada. Quando uma atividade é destruída, o sistema não retém o estado da atividade.

    Esse comportamento é diferente para atividades raiz da tela de início quando o app está em execução em um dispositivo com o Android 12 ou versões mais recentes.

  • As atividades podem ser instanciadas diversas vezes, mesmo de outras tarefas.

Gerencie tarefas

O Android gerencia as tarefas e a backstack posicionando todas as atividades iniciadas em sequência na mesma tarefa, em uma última pilha a entrar, a primeira a sair. Isso funciona muito bem para a maioria dos apps, e você geralmente não precisa se preocupar com como suas atividades são associadas às tarefas ou como elas existem na backstack.

No entanto, você pode decidir que deseja interromper o comportamento normal. Por exemplo, você pode querer que uma atividade no app inicie uma nova tarefa quando for iniciada, em vez de ser colocada dentro da tarefa atual. Ou, ao iniciar uma atividade, convém trazer uma instância já existente dela, em vez de criar uma nova instância na backstack. Ou talvez você queira que a backstack limpe todas as atividades, exceto a atividade raiz, quando o usuário sair da tarefa.

Você pode fazer isso e muito mais usando atributos no elemento de manifesto <activity> e flags na intent transmitida para startActivity().

Estes são os principais atributos <activity> que podem ser usados para gerenciar tarefas:

E estas são as principais flags de intent que você pode usar:

As seções a seguir discutem como usar esses atributos de manifesto e sinalizações de intent para definir como as atividades são associadas às tarefas e como elas se comportam na backstack.

Também discutimos as considerações sobre como tarefas e atividades são representadas e gerenciadas na tela "Recentes". Normalmente, você permite que o sistema defina como a tarefa e as atividades são representadas na tela Recentes e não precisa modificar esse comportamento. Para mais informações, consulte Tela Recentes.

Definir modos de inicialização

Os modos de inicialização permitem definir como uma nova instância de uma atividade é associada à tarefa atual. Você pode definir os modos de inicialização de duas maneiras, que são descritas nas seções a seguir:

Portanto, se a atividade A iniciar a atividade B, ela poderá definir no manifesto como será associada à tarefa atual, e a atividade A poderá usar uma flag de intent para solicitar como a atividade B pode se associar à tarefa atual.

Se as duas atividades definirem como a Atividade B se associa a uma tarefa, a solicitação da Atividade A, conforme definido na intent, será cumprida em relação à solicitação da Atividade B, conforme definido no manifesto.

Definir modos de inicialização usando o arquivo de manifesto

Ao declarar uma atividade no arquivo de manifesto, você pode especificar como a atividade é associada a uma tarefa usando o atributo launchMode do elemento <activity>.

Há cinco modos de inicialização que podem ser atribuídos ao atributo launchMode:

  1. "standard"
    O modo padrão. O sistema cria uma nova instância da atividade na tarefa em que foi iniciada e encaminha a intent a ela. A atividade pode ser instanciada várias vezes, cada instância pode pertencer a tarefas diferentes, e uma tarefa pode ter várias instâncias.
  2. "singleTop"
    Se uma instância da atividade já existir na parte de cima da tarefa atual, o sistema vai encaminhar a intent a essa instância com uma chamada para o método onNewIntent(), em vez de criar uma nova instância da atividade. A atividade é instanciada várias vezes, cada instância pode pertencer a tarefas diferentes, e uma tarefa pode ter várias instâncias, mas somente se a atividade na parte superior da backstack não for uma instância existente da atividade.

    Por exemplo, suponha que a backstack de uma tarefa consiste na atividade raiz A com as atividades B, C e D no topo. Portanto, a pilha é A-B-C-D, com D no topo. Uma intent chega a uma atividade do tipo D. Se D tiver o modo de inicialização "standard" padrão, uma nova instância da classe será iniciada, e a pilha se tornará A-B-C-D-D. No entanto, se o modo de inicialização de D for "singleTop", a instância existente de D receberá a intent por onNewIntent(), porque está no topo da pilha e ela permanece como A-B-C-D. Por outro lado, se uma intent chegar para uma atividade do tipo B, uma nova instância de B será adicionada à pilha, mesmo que o modo de inicialização seja "singleTop".

  3. "singleTask"
    O sistema cria a atividade na raiz de uma nova tarefa ou localiza a atividade em uma tarefa já existente com a mesma afinidade. Se uma instância da atividade já existir, o sistema encaminhará a intent para a instância existente com uma chamada para o método onNewIntent(), em vez de criar uma nova instância. Enquanto isso, todas as outras atividades acima dela são destruídas.
  4. "singleInstance".
    O comportamento é o mesmo da "singleTask", exceto pelo fato de o sistema não iniciar nenhuma outra atividade na tarefa que contém a instância. A atividade é sempre o único membro da tarefa. Qualquer atividade iniciada por esse será aberta em uma tarefa separada.
  5. "singleInstancePerTask".
    A atividade só pode ser executada como a atividade raiz da tarefa, a primeira que a criou. Portanto, só pode haver uma instância dessa atividade em uma tarefa. Diferentemente do modo de inicialização singleTask, essa atividade pode ser iniciada em várias instâncias em tarefas diferentes se a sinalização FLAG_ACTIVITY_MULTIPLE_TASK ou FLAG_ACTIVITY_NEW_DOCUMENT estiver definida.

Como outro exemplo, o app Navegador do Android declara que a atividade do navegador sempre é aberta na própria tarefa, especificando o modo de inicialização singleTask no elemento <activity>. Isso significa que, se o app emitir uma intent para abrir o navegador Android, a atividade dele não será colocada na mesma tarefa que o app. Em vez disso, uma nova tarefa será iniciada no navegador ou, se o navegador já tiver uma tarefa em execução em segundo plano, essa tarefa será encaminhada para processar a nova intent.

Independentemente de uma atividade começar em uma nova tarefa ou na mesma tarefa que a que a iniciou, o botão "Voltar" e o gesto sempre levam o usuário à atividade anterior. No entanto, se você iniciar uma atividade que especifica o modo de inicialização singleTask e uma instância dessa atividade existir em uma tarefa em segundo plano, toda a tarefa será colocada em primeiro plano. Nesse ponto, a backstack inclui todas as atividades da tarefa trazidas para o topo da pilha. A Figura 4 mostra esse tipo de cenário.

Figura 4. Uma representação de como uma atividade com o modo de inicialização "singleTask" é adicionada à backstack. Se a atividade já fizer parte de uma tarefa em segundo plano com a própria backstack, toda essa backstack também vai para frente, sobre a tarefa atual.

Para saber mais sobre o uso de modos de inicialização no arquivo de manifesto, consulte a documentação do elemento <activity>.

Definir modos de inicialização usando flags de intent

Ao iniciar uma atividade, você pode modificar a associação padrão de uma atividade à tarefa incluindo flags na intent entregue a startActivity(). As sinalizações que podem ser usadas para modificar o comportamento padrão são:

FLAG_ACTIVITY_NEW_TASK

O sistema inicia a atividade em uma nova tarefa. Se uma tarefa já estiver em execução para a atividade iniciada, ela será colocada em primeiro plano com o último estado restaurado, e a atividade receberá a nova intent em onNewIntent().

Isso produz o mesmo comportamento que o valor "singleTask" launchMode discutido na seção anterior.

FLAG_ACTIVITY_SINGLE_TOP

Se a atividade iniciada for a atual, na parte de cima da backstack, a instância atual vai receber uma chamada para onNewIntent() em vez de criar uma nova instância da atividade.

Isso produz o mesmo comportamento que o valor "singleTop" launchMode discutido na seção anterior.

FLAG_ACTIVITY_CLEAR_TOP

Se a atividade iniciada já estiver em execução na tarefa atual, em vez de iniciar uma nova instância dessa atividade, o sistema vai destruir todas as outras atividades acima dela. A intent é entregue à instância retomada da atividade, agora na parte de cima, pelo onNewIntent().

Não há valor para o atributo launchMode que produza esse comportamento.

FLAG_ACTIVITY_CLEAR_TOP é usado com mais frequência em conjunto com FLAG_ACTIVITY_NEW_TASK. Quando usadas juntas, essas flags localizam uma atividade existente em outra tarefa e a colocam em uma posição em que ela possa responder à intent.

Processar afinidades

Uma afinidade indica a que tarefa uma atividade "prefere" pertencer. Por padrão, todas as atividades do mesmo app têm afinidade entre si: elas "preferem" estar na mesma tarefa.

Entretanto, você pode modificar a afinidade padrão de uma atividade. As atividades definidas em apps diferentes podem compartilhar uma afinidade, e as atividades definidas no mesmo app podem receber diferentes afinidades de tarefa.

É possível modificar a afinidade de uma atividade usando o atributo taskAffinity do elemento <activity>.

O atributo taskAffinity usa um valor de string que precisa ser diferente do nome de pacote padrão declarado no elemento <manifest>, já que o sistema usa esse nome para identificar a afinidade de tarefa padrão do app.

A afinidade tem relevância em duas circunstâncias:

  1. Quando a intent que inicia uma atividade contém a flag FLAG_ACTIVITY_NEW_TASK.

    Por padrão, uma nova atividade é iniciada na tarefa da atividade que chamou startActivity(). Ela é colocada na mesma backstack que o autor da chamada.

    No entanto, se a intent transmitida para startActivity() contiver a flag FLAG_ACTIVITY_NEW_TASK, o sistema vai procurar uma tarefa diferente para hospedar a nova atividade. Muitas vezes, essa é uma nova tarefa. No entanto, não precisa ser. Se houver uma tarefa com a mesma afinidade da nova atividade, ela será iniciada nela. Caso contrário, ela iniciará uma nova tarefa.

    Se essa flag fizer com que uma atividade inicie uma nova tarefa e o usuário use o botão home ou um gesto para sair, é necessário que haja alguma maneira de voltar à tarefa. Algumas entidades, como o gerenciador de notificações, sempre iniciam atividades em uma tarefa externa, nunca como parte da própria tarefa. Portanto, sempre colocam FLAG_ACTIVITY_NEW_TASK nas intents que passam para startActivity().

    Se uma entidade externa que pode usar essa flag puder invocar sua atividade, verifique se o usuário tem uma maneira independente de voltar à tarefa que foi iniciada, como um ícone na tela de início, em que a atividade raiz da tarefa tem um filtro de intent CATEGORY_LAUNCHER. Para mais informações, consulte a seção sobre como iniciar tarefas.

  2. Quando uma atividade tem o atributo allowTaskReparenting definido como "true".

    Nesse caso, a atividade pode passar da tarefa em que foi iniciada para a tarefa com a qual ela tem afinidade quando chegar ao primeiro plano.

    Por exemplo, suponha que uma atividade que informe as condições climáticas em cidades selecionadas seja definida como parte de um app de viagens. Ela tem a mesma afinidade de outras atividades no mesmo app, a afinidade do app padrão e pode ter esse atributo como elemento pai.

    Quando uma das suas atividades inicia a atividade de informações sobre o clima, ela inicialmente pertence à mesma tarefa que sua atividade. No entanto, quando a tarefa do app de viagens fica em primeiro plano, a atividade de informações sobre o clima é reatribuída a ela e exibida.

Limpar a backstack

Se o usuário sair de uma tarefa por muito tempo, o sistema apaga a tarefa de todas as atividades, exceto a atividade raiz. Quando o usuário retorna à tarefa, somente a atividade raiz é restaurada. O sistema se comporta dessa maneira com base na suposição de que, após um período prolongado, os usuários abandonaram o que estavam fazendo antes e estão retornando à tarefa para começar algo novo.

Há alguns atributos de atividade que podem ser usados para modificar esse comportamento:

alwaysRetainTaskState
Quando esse atributo é definido como "true" na atividade raiz de uma tarefa, o comportamento padrão que acabamos de descrever não acontece. A tarefa retém todas as atividades na pilha mesmo após um longo período.
clearTaskOnLaunch

Quando esse atributo é definido como "true" na atividade raiz de uma tarefa, a tarefa é limpa na atividade raiz sempre que o usuário sai da tarefa e retorna a ela. Em outras palavras, é o oposto de alwaysRetainTaskState. O usuário sempre retorna à tarefa no estado inicial, mesmo depois de deixar a tarefa por apenas um momento.

finishOnTaskLaunch

Esse atributo é como clearTaskOnLaunch, mas opera em uma única atividade, não em uma tarefa inteira. Ele também pode fazer com que qualquer atividade seja concluída, exceto a atividade raiz. Quando ele é definido como "true", a atividade permanece parte da tarefa apenas para a sessão atual. Se o usuário sair e depois retornar à tarefa, ela não estará mais presente.

Iniciar uma tarefa

Você pode configurar uma atividade como o ponto de entrada de uma tarefa concedendo a ela um filtro de intent com "android.intent.action.MAIN" como a ação especificada e "android.intent.category.LAUNCHER" como a categoria especificada:

<activity ... >
    <intent-filter ... >
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
    ...
</activity>

Um filtro de intent desse tipo faz com que um ícone e um rótulo para a atividade sejam mostrados no Acesso rápido aos apps, oferecendo aos usuários uma maneira de iniciar a atividade e retornar à tarefa criada a qualquer momento após a inicialização.

Essa segunda habilidade é importante. Os usuários precisam conseguir sair de uma tarefa e voltar a ela mais tarde usando essa tela de início de atividades. Por esse motivo, use apenas os dois modos de inicialização que marcam atividades como sempre iniciando uma tarefa, "singleTask" e "singleInstance", quando a atividade tiver um filtro ACTION_MAIN e um CATEGORY_LAUNCHER.

Imagine, por exemplo, o que poderia acontecer se o filtro estivesse ausente: uma intent inicia uma atividade "singleTask", uma nova tarefa e o usuário passa algum tempo trabalhando nessa tarefa. Em seguida, o usuário usa o botão home ou o gesto. A tarefa será enviada para o segundo plano e não ficará visível. Agora, o usuário não pode retornar à tarefa, porque ela não está representada no Acesso rápido aos apps.

Para os casos em que você não quer que o usuário retorne a uma atividade, defina o finishOnTaskLaunch do elemento <activity> como "true". Para mais informações, consulte a seção sobre como limpar a backstack.

Mais informações sobre como tarefas e atividades são representadas e gerenciadas na tela "Recentes" estão disponíveis na tela Recentes.

Mais recursos