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 mostrar essa mensagem. Essa nova atividade é adicionada à backstack. Então, quando o usuário toca ou gesticula "Voltar", essa nova atividade é concluída e retirada da pilha.

Ciclo de vida de uma tarefa e a backstack dela

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 no Acesso rápido aos apps ou na tela inicial, a tarefa desse app fica em primeiro plano. Se nenhuma tarefa existir para o app, uma nova tarefa será criada e a atividade principal desse app será aberta como a atividade raiz na pilha.

Quando a atividade atual inicia outra, a nova atividade é empurrada para a parte de cima 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 da parte superior 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 retiradas da pilha quando são iniciadas pela atividade atual e dispensadas pelo usuário com o botão ou o gesto "Voltar". Portanto, a pilha de retorno opera como uma estrutura de objeto último a chegar, primeiro a sair. A Figura 1 mostra uma linha do tempo com 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 deixa de existir.

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

Atividades raiz da tela de início são aquelas que declaram um filtro de intent com ACTION_MAIN e CATEGORY_LAUNCHER. Essas atividades são exclusivas porque funcionam como pontos de entrada para o app pelo Acesso rápido aos apps e são usadas para iniciar uma tarefa.

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

Comportamento do sistema no Android 11 e versões anteriores
O sistema finalizará 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 navegar 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 a frio.

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

No entanto, se o app modificar onBackPressed() para processar a navegação de retorno e concluir a atividade, atualize a implementação para chamar super.onBackPressed() em vez de concluir. 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 na tarefa são interrompidas, mas a backstack da tarefa permanece intacta: a tarefa perde o foco enquanto outra tarefa ocorre, como 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 abaixo 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 é exibida, a Tarefa A entra em 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 ao início 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 intas, 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 página 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 dela será criada e enviada para a pilha, em vez de levar qualquer instância anterior para o topo. Assim, uma atividade no seu 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 "Voltar", as instâncias da atividade serão reveladas na ordem em que foram abertas, cada uma com o próprio estado da 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 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" na atividade B, a atividade A será retomada com o estado restaurado.

  • Quando o usuário sai de uma tarefa usando o botão ou o gesto home, a atividade atual é interrompida e a tarefa fica em segundo plano. O sistema retém o estado de cada atividade na tarefa. Se o usuário retomar a tarefa selecionando o ícone na tela de início que a iniciou, ela passará 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 tarefas e a backstack colocando 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 as 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 seu 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 em vez de criar uma nova instância sobre a backstack. Ou você pode querer que a backstack limpe todas as atividades, exceto a atividade raiz, quando o usuário sai da tarefa.

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

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

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 flags de intent para definir como as atividades se associam à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. É possível definir modos de inicialização de duas maneiras, descritas nas seções a seguir:

Portanto, se a Atividade A iniciar a Atividade B, a Atividade B poderá definir no manifesto como se associará à tarefa atual, e a Atividade A poderá usar uma sinalização de intent para solicitar como a Atividade B 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á honrada 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 seu arquivo de manifesto, você pode especificar como ela é 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 da qual 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 de cima da backstack não for uma instância 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 (de forma que a pilha seja A-B-C-D, com D na parte de cima). 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 vai receber a intent por onNewIntent(), porque está na parte de cima da pilha, e ela vai continuar sendo 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 em cima dele são destruídas.
  4. "singleInstance".
    O comportamento é o mesmo da "singleTask", mas o sistema não inicia nenhuma outra atividade na tarefa que contém a instância. A atividade é sempre o único membro da tarefa. Todas as atividades iniciadas por ele são abertas 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. Diferente 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 Android declara que a atividade do navegador da Web 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 do app. Em vez disso, uma nova tarefa será iniciada para o 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 apresentadas na parte de cima 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 sinalizações de intent

Ao iniciar uma atividade, você pode modificar a associação padrão de uma atividade à tarefa incluindo flags na intent entregue ao 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 que está sendo 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 vai receber uma chamada para onNewIntent() em vez de criar uma nova.

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 superior, por meio de onNewIntent().

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

O FLAG_ACTIVITY_CLEAR_TOP é mais usado 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 qual 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, é possível modificar a afinidade padrão de uma atividade. Atividades definidas em apps diferentes podem compartilhar uma afinidade, e atividades definidas no mesmo app podem receber diferentes afinidades de tarefa.

Você pode 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>, porque 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 sinalização FLAG_ACTIVITY_NEW_TASK.

    Por padrão, uma nova atividade é iniciada na tarefa da atividade que chamou startActivity(). Ele é enviado para a mesma backstack do autor da chamada.

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

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

    Se uma entidade externa que possa usar essa flag possa invocar sua atividade, verifique se o usuário tem uma maneira independente de voltar à tarefa 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 começa para a tarefa com que 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 viagem. Ela tem a mesma afinidade de outras atividades no mesmo app, a afinidade do app padrão e pode ser associada a esse atributo novamente.

    Quando uma das suas atividades inicia a atividade de denúncia de clima, ela inicialmente pertence à mesma tarefa que a atividade. No entanto, quando a tarefa do app de viagem fica em primeiro plano, a atividade de previsão do tempo é reatribuída a ela e exibida.

Limpar a backstack

Se o usuário sair de uma tarefa por muito tempo, o sistema vai limpar a tarefa de todas as atividades, exceto a raiz. Quando o usuário retorna à tarefa, somente a atividade raiz é restaurada. O sistema se comporta dessa maneira com base no princípio de que, depois de um tempo estendido, 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 é liberada para a atividade raiz sempre que o usuário sai e retorna a ela. Em outras palavras, é o oposto de alwaysRetainTaskState. O usuário sempre retorna à tarefa no estado inicial, mesmo depois de sair da tarefa por apenas um momento.

finishOnTaskLaunch

Esse atributo é como clearTaskOnLaunch, mas opera em uma única atividade, não em uma tarefa inteira. Isso 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 na sessão atual. Se o usuário sair e retornar à tarefa, ela não estará mais presente.

Iniciar uma tarefa

É possível 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 exibidos 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 esse inicializador 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", iniciando 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 tem como 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 ver 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