Caso prefira, você encontrará todo esse material, em inglês, no site do Developer Android. A tradução e comentários dos materiais eu faço livremente para ajudar a comunidade que fala português.

sábado, 26 de março de 2011

Atividades - Gerenciando Tarefas

A forma como o Android gerencia as tarefas e o back stack - colocando as atividades em uma sucessão na mesma tarefa e usando o conceito last in, first out - funciona perfeitamente para a maioria das aplicações e você não deveria se preocupar sobre como as atividades são associadas às tarefas ou como elas existem no back stack. Contudo, você deve decidir se quer interromper o comportamento padrão. Talvez você queira que uma atividade em sua aplicação comece uma nova tarefa quando ela é iniciada (ao invés de ser colocada como a tarefa corrente); ou, quando você inicia uma atividade, você queira trazer para a frente uma instância existente dela (ao invés de criar uma nova instância no topo do back stack); ou talvez você queira que seu back stack sera limpo de todas as atividades e só tenha uma atividade (a atividade root) quando o usuário saia da tarefa.

Você pode fazer isso e muitas outras coisas usando o atributo <activity> que se encontra no manifesto do Android e com flags em seus intents que você passa para o startActivity().

Nesse sentido, os atributos principals de <activity> que você pode usar são:

taskAffinity
launchMode
allowTaskReparenting
clearTaskOnLaunch
alwaysRetainsTaskState
finishOnTaskLaunch

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

FLAG_ACTIVITY_NEW_TASK
FLAG_ACTIVITY_CLEAR_TOP
FLAG_ACTIVITY_SINGLE_TOP

Definindo os modos de lançamento (launch modes)

Launch modes permitem a você definir ua nova instância de uma atividade que está associada à tarefa corrente. Você pode definir diferentes launch modes de duas maneiras:

Usando o arquivo de manifesto
Quando você declara uma atividade em seu arquivo de manifesto, você pode manifestar como a atividade deverá ser associada com tarefa quando ela se inicia

Usando intent flags
Quando você chama startActivity(), você pode incluir a flag dentro do Intent que declara como (ou se) uma nova atividade deverá ser associada com a tarefa corrente.

Ou seja, se a atividade A inicia a atividade B, a atividade B pode definir em seu manifesto como ela deverá ser associada com a tarefa corrente e a atividade A pode também requisitar como a atividade B deve ser associada com a tarefa corrente. Se ambas atividades definem como a atividade B deveria ser associada com a tarefa, então a requisição da atividade A (como definida em seu intent) é honrada sobre a requisição da atividade B.

Usando o arquivo de manifesto

Quando se declara uma atividade no arquivo de manifesto, você pode especificar como a atividade estará associada com a tarefa usando o elemento <activity> dentro do atributo launchMode.

O atributo launchMode especifica uma instrução sobre como a atividade deve ser lançada dentro da tarefa. Existem quatro tipos diferentes de launch modes que você pode assinalar no atributo launchMode:

"standard" (o modo padrão)
O sistema cria uma nova instância da atividade na tarefa a partir da tarefa já existente e faz a rota do intent para ele. A atividade pode ser instanciada multiplas vezes e cada instância pode pertencer a diferentes tarefas e uma tarefa pode ter multiplas intâncias.

"singleTop"
Se uma instância de uma atividade já existe no topo da atividade corrente, o sistema faz o roteamento do intent para a instância através de uma chamada para o método onNewIntent(), ao invés de criar uma nova instância da atividade. A atividade pode ser instanciada multiplas vezes e cada instância pode pertencer a tarefas diferentes e uma tarefa pode ter multiplas instâncias (mas apenas se a atividade que estiver no topo do back stack não for uma instância existente da atividade).

Por exemplo, suponha que o back stack de uma tarefa consista da atividade raiz de A com as atividades B, C e D no topo (o stack ficaria como A-B-C-D onde D está no topo). Um intent chega para a atividade do tipo D. Se D tem o modo de launch mode em standard, uma nova instância da classe é lançada e o back stack se torna A-B-C-D-D. Contudo, se a atividade D estiver em modo singleTop, a instância existente de D é entregue ao intent através de onNewIntent(), já que está no topo do back stack - o back stack continua como A-B-C-D. Mas, se um intent chega para a atividade do tipo B, então uma nova instância de B é adicionada ao back stack, mesmo que o launch mode esteja em singleTop, fazendo com que o back stack fique como A-B-C-D-B.

"singleTask"
O sistema cria uma nova tarefa e instancia a atividade na raiz da nova tarefa. Contudo, se uma instância da atividade já existe em uma tarefa separada, o sistema faz o roteamento do intent para a instância existente através de uma chamada para o método onNewIntent(), ao invés de criar uma nova instância. Apenas uma instância de cada atividade pode existir a cada vez.

"singleInstance"
O mesmo que singleTask, exceto que o sistema não lança nenhuma outra atividade dentro da tarefa que está relacionada à instância. A atividade é simple única e o único membro de sua tarefa; quaisquer outras atividades iniciadas são apenas em uma tarefa separada.

Como outro exemplo, a aplicação de navegador do Android declara que a atividade de navegação deve sempre abrir sua própria tarefa - ela especifica que é singleTask no lanch mode em seu elemento <activity>. Isso significa que se sua aplicação tem um intent para abrir o navegador, a atividade não é colocada na mesma tarefa de sua aplicação. Ao invés disso, ele inicia uma tarefa nova apenas para o navegador ou, se o navegador já está aberto, a tarefa é chamada para a tela para receber a requisição.

Independente da atividade iniciar uma nova tarefa ou não ou a mesma tarefa como atividade ser aberta, o BACK sempre volta para a atividade anterior. Apesar disso ser verdade, se você iniciar uma atividade a partir de sua tarefa (Task A) que especifica o launch mode como singleTask, então a sua atividade deve ter uma instância no background que pertença a tarefa com seu próprio back stack (Task B). Nesse caso, quando a Task B for lançada para a tela para receber o novo intent, o BACK navega por todas as atividades da Task B antes de retornar para a atividade que está no topo da Task A. A imagem abaixo ilustra esse cenário.


Usando Intent Flags

Quando você inicia uma atividade, você pode modificar a associação padrão de uma atividade para que sua tarefa incluindo flags de intent que são entregues no startActivity(). As flags que você pode usar para modificar o comportamento padrão são:

FLAG_ACTIVITY_NEW_TASK
Inicia a atividade em uma nova tarefa. Se a tarefa já está rodando para a atividade que você está iniciando agora, a tarefa é trazida para a frente com seu último estado restaurado e a atividade receber um novo intent em onNewIntent().

Essa flag produz o mesmo comportamento de "singleTask".

FLAG_ACTIVITY_SINGLE_TOP
Se uma atividade se inicia na atividade corrente (no topo do back stack), então a instância existente recebe a chamada para onNewIntent(), ao invés de criar uma nova instância da atividade.

Essa flag produz o mesmo comportamento de "singleTop".

FLAG_ACTIVITY_CLEAR_TOP
Se a atividade iniciada já está rodando na tarefa corrente, então ao invés de lançar uma nova instância da atividade, todas as outras atividades no topo são destruídas e o intent é entregue para a instância reiniciada da atividade (que agora está no topo), através do onNewIntent().

Não existe nenhum atributo de launchMode que produza esse comportamento.

Essa flag é mais comumente usada em conjunção com FLAG_ACTIVITY_NEW_TASK. Quando usadas em conjunto, essas flags são uma forma de localizar uma atividade existente em outra tarefa e colocá-la em uma posição onde possa responder a um intent.

Gerenciando afinidades

Uma affinity (afinidade) indica para qual tarefa uma atividade prefere pertencer. Por padrão, todas as atividades de uma mesma aplicação tem afinidade umas às outras. Então, por padrão, todas as atividades de uma aplicação preferem estar dentro da mesma tarefa. Mas, você pode modificar a afinidade padrão de uma atividade. Atividades definidas em aplicações diferentes podem compartilhar uma afinidade, ou atividades definidas na mesma aplicação podem ser marcadas para ter afinidades distintas.

Você pode modificar a atividade de qualquer dada atividade com o atributo taskAffinity dentro do elemento <activity>.

O atributo taskAffinity recebe um valor string que deve ser único do nome de pacote padrão declarado no elemento <manifest>, pois o sistema usa o nome para identificar a tarefa padrão de afinidade da aplicação.

A afinidade deve ser usada em duas circunstâncias:

  • Quando um intent que lança uma atividade contém a flag FLAG_ACTIVITY_NEW_TASK.

    Uma nova atividade é, por padrão, lançada dentro da tarefa da atividade que chamou o startActivity(). Ela é colocada no mesmo back stack de quem fez a sua chamada. Mas, se o intent passado para o startActivity() contém a flag FLAG_ACTIVITY_NEW_TASK, o sistema procura por uma tarefa diferente para hospedar a nova atividade. Em muitos casos, é uma nova tarefa a ser criada. Só que não precisa ser assim. Se já existe uma tarefa com a mesma afinidade da nova atividade, a atividade é lançada nessa tarefa. Se não existe, então é criada uma nova tarefa.

    Se essa flag fizer com que a atividade seja criada numa nova tarefa e o usuário pressionar o botão HOME, então deve haver alguma maneira do usuário navegar de volta à tarefa. Algumas entidades (como o gerenciador de notificação) sempre inicia atividades em uma tarefa externa, nunca como parte de sua própria tarefa e, por conta disso, eles sempre colocar a flag de nova tarefa em seus intents quando eles chamam startActivity(). Se você tem uma atividade que pode ser chamada por uma entidade externa que use essa flag, tome cuidado para que o usuário tenha uma maneira de voltar para a tarefa que foi iniciada.
  • Quando uma atividade tem o atributo allowTaskReparenting setado para true.

    Nesse caso, a atividade pode mover-se da tarefa que ela iniciou para uma tarefa que tenha afinidade quando a tarefa vai para a tela.

    Por exemplo, suponha que uma atividade que reporte as condições de tempo em cidades selecionadas sejam definidas como parte de uma aplicação de viagens. Ela tem a mesma afinidade de outras atividades na mesma aplicação e permite o re-parenting em seus atributos. Quando uma das atividades inicia a atividade de tempo, ele inicialmente pertence à mesma tarefa de sua atividade. Contudo, quando a tarefa da aplicação de viagens vem para a frente, a atividade de tempo é reassinalada (re-parented) para essa tarefa e mostrada dentro dela.
Limpando o back stack

Se o usuário deixar uma tarefa por um longo período, o sistema limpa a tarefa de todas as atividades exceto a atividade raiz. Quando o usuário retorna à tarefa, apenas a atividade raiz é restaurada. O sistema se comporta dessa maneira pois após um determinado tempo é muito provável que o usuário tenha abandonado o que eles estavam fazendo e iniciaram outra tarefa.

Existem alguns atributos de atividade que podem mudar esse comportamento:

alwaysRetainTaskState
Se esse atributo está setado como true na raiz da tarefa, o comportamento padrão mencionado acima não acontece. A tarefa mantém todas as atividades no stack mesmo após um longo período.

clearTaskOnLaunch
Se esse atributo está setado como true na raiz da tarefa, o stack é limpo e só mantém a atividade raiz quando o usuário deixa a tarefa e retorna a ela. Em outras palavras, é o oposto de alwaysRetainTaskState. O usuário sempre retorna à tarefa em seu estado inicial, mesmo depois de deixar a tarefa por apenas alguns instantes.

finishOnTaskLaunch
Esse atributo é parecido com clearTaskOnLaunch mas opera como uma atividade única e não uma tarefa. Ela pode inclusive causar a destruição da atividade incluindo a atividade raiz. Quando está setado para true, a atividade permanece parte da tarefa apenas dentro da sessão corrente. Se o usuário sair da tarefa e retornar, mesmo que imediatamente, a atividade não mais estará presente por lá.

Iniciando uma tarefa

Você pode configurar qual atividade é o ponto de entrada de uma tarefa provendo um intent filter com android.intent.action.MAIN como ação específica e android.intent.category.LAUNCHER como categoria específicada. 
<activity ... >
    <intent-filter ... >
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
    ...
</activity>
Um intent filter dessa natureza faz com que o ícone e o label da atividade sejam mostrados no launcher da aplicação, dando aos usuários uma maneira de lançar a atividade e retornar para a tarefa que ela cria a qualquer momento que seja lançada.

A segunda habilidade é importante: usuários podem ser permitidos a deixar a tarefa e então voltar para ela usando o activity launcher. Por essa razão, os launch modes singleTask e singleInstance devem ser usadas apenas quando a atividade tem um filtro ACTION_MAIN e um filtro CATEGORY_LAUNCHER. Imagine, por exemplo, o que pode acontecer caso um filtro desses esteja faltando: um intent lança uma atividade singleTask, iniciando uma nova tarefa e o usuário fica um tempo nela. O usuário então pressiona a tecla HOME. A tarefa é mandada para o background e fica invisível. Como não é representada no launcher, o usuário não tem mais como retornar à tarefa.

Agora, em casos em que você não quer que o usuário possa retornar à aplicação, configure o elemento finishOnTaskLaunch como true.

0 comentários:

Portions of this page are modifications based on work created and shared by the Android Open Source Project and used according to terms described in the Creative Commons 2.5 Attribution License.
Related Posts Plugin for WordPress, Blogger...