Intenção - se a atividade estiver em execução, leve-a para a frente, caso contrário, inicie uma nova (a partir da notificação)

129

Meu aplicativo tem notificações, que - obviamente - sem sinalizadores, iniciam uma nova atividade toda vez, para que eu receba várias mesmas atividades sendo executadas umas sobre as outras, o que está errado.

O que eu quero fazer é trazer a atividade especificada nas notificações com intenção pendente para a frente, se já estiver em execução, caso contrário, inicie-a.

Até agora, a intenção / intenção pendente para essa notificação que tenho é

private static PendingIntent prepareIntent(Context context) {
    Intent intent = new Intent(context, MainActivity.class);
    intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);

    return PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}

e estranhamente, às vezes funciona, às vezes não ... Sinto que já tentei todas as combinações de sinalizadores.

urSus
fonte

Respostas:

131

Você pode usar isto:

<activity 
   android:name=".YourActivity"
   android:launchMode="singleTask"/>

que funcionará de maneira semelhante, "singleInstance"mas não terá essa animação estranha.

Franco
fonte
15
+1, você deve ser a resposta aceita. As diferenças estão aqui: developer.android.com/guide/topics/manifest/…
user1032613
1
Para esclarecer, use singleTask se você tiver várias atividades no aplicativo e este for o pai, singleInstance se você tiver apenas uma Atividade.
Frostymarvelous
4
Se alguém como eu já tentou várias combinações de soluções antes de terminar neste site: lembre-se de se livrar de outras alterações que você tentou e faça essa alteração na atividade correta. Levei muito tempo para descobrir que esta solução teria funcionado se eu não tinha tentado outras soluções, bem como (por exemplo, substituir onNewIntente definir sinalizadores)
lucidbrot
Eu tenho um mapa do Messenger, par de atividades. Quero exibir a instância Activity se receber uma mensagem dela. Existe alguma maneira de fazer isso. por exemplo: HashMap.get (Messenger) .bringActivityToFront (); Quero dizer, a atividade ainda está em execução. Se eu abrir uma tarefa recente e clicar nela, ela irá onResume (); Não consigo ativar onResume () programaticamente.
@JaveneCPPMcGowan que parece ser uma pergunta completamente diferente, sugiro que você publique que, como é sua própria pergunta, infelizmente não sei a resposta.
Franco
46

Eu acho que a melhor maneira de fazê-lo e de uma maneira simples é iniciar a atividade normalmente, mas definir essa atividade no manifesto com a singleInstancepropriedade Com isso, você praticamente aborda os dois problemas que está enfrentando no momento, trazendo a atividade para a frente o tempo todo e permitindo que o sistema operacional crie automaticamente uma nova, se não houver atividade, ou traga para a frente a atividade existente no momento (graças ao singleInstancepropriedade).

É assim que uma atividade é declarada como uma única instância:

<activity 
   android:name=".YourActivity"
   android:launchMode="singleInstance"/>

Além disso, para evitar uma animação instável ao iniciar o singleInstance, você pode usar o "singleTask", ambos são muito semelhantes, mas a diferença é explicada aqui conforme a documentação do Google:

<activity 
   android:name=".YourActivity"
   android:launchMode="singleTask"/>

singleInstance é o mesmo que "singleTask", exceto que o sistema não inicia nenhuma outra atividade na tarefa que contém a instância. A atividade é sempre o único e único membro de sua tarefa.

Espero que isto ajude.

Saudações!

Martin Cazares
fonte
4
Parece funcionar, mas agora todos os outros lançamento nova atividade I do singleIntent MainActivity, reproduzir esta animação arco estranho em vez do desvanecimento normal e escala
Ursus
Que tal eu lançar uma nova atividade sempre, mas limpar todas as outras instâncias em execução dela, isso seria possível?
Ursus
Bem, eu não acho que a animação que você mencionou tenha algo a ver com a propriedade "singleInstance", tudo o que faz é reutilizar a instância existente, talvez a animação da qual você está falando seja uma questão completamente diferente ...
Martin Cazares,
1
bem, eu estou testando agora e ele faz. Se eu remover todos os sinalizadores de intenção e apenas definir o launchMode = "singleInstance" no manifesto, a animação estranha estará lá, se eu removê-lo, voltará ao normal
urSus
Sim, de acordo com a documentação android, parece que a animação "init" não está mais lá, como de costume, porque a atividade não está sendo inicializado, está apenas a ser reutilizado ...
Martin Cazares
26

Eu acho que o que você precisa está dentro singleTop Activity, em vez de um singleTaskou singleInstance.

<activity android:name=".MyActivity"
          android:launchMode="singleTop"
          ...the rest... >

O que a documentação diz atende perfeitamente às suas necessidades:

[...] uma nova instância de uma singleTopatividade " " também pode ser criada para lidar com uma nova intenção. No entanto, se a tarefa de destino já tiver uma instância existente da atividade no topo de sua pilha, essa instância receberá a nova intenção (em uma onNewIntent()chamada); uma nova instância não é criada. Em outras circunstâncias - por exemplo, se uma instância existente da singleTopatividade " " estiver na tarefa de destino, mas não no topo da pilha, ou se estiver no topo de uma pilha, mas não na tarefa de destino - um novo instância seria criada e enviada na pilha.

Além disso (sem trocadilhos), eu tinha exatamente a mesma necessidade que você. Testei todos os launchModesinalizadores para descobrir como eles realmente se comportam na prática e, como resultado, singleTopé realmente o melhor para isso: nenhuma animação estranha, aplicativo exibido uma vez na lista de aplicativos recentes (ao contrário do singleInstanceque é exibido duas vezes devido ao fato de que não ' permitir que qualquer outro faça Activityparte de sua tarefa) e se comportar adequadamente, independentemente de o alvo Activityjá existir ou não.

Shlublu
fonte
2
Trabalho até 22:00 na noite de sexta-feira e é isso que eu quero ... Triste vida do desenvolvedor.
Felixwcf
Economizou muito do meu tempo! Obrigado!
Hetsgandhi 03/10/19
13

Este é um tópico antigo, mas para todos aqueles que ainda procuram respostas, aqui está a minha solução. É puramente no código, sem configurações de manifesto:

private static PendingIntent prepareIntent(Context context) {
  Intent intent = new Intent(context, MainActivity.class);
  intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);      
  return PendingIntent.getActivity(context, NON_ZERO_REQUEST_CODE, intent, 
    PendingIntent.FLAG_UPDATE_CURRENT);
}

Aqui, FLAG_ACTIVITY_SINGLE_TOP abre a atividade existente se ela estiver no topo da pilha de tarefas. Se não estiver no topo, mas dentro da pilha, FLAG_ACTIVITY_CLEAR_TOP removerá todas as atividades no topo da atividade de destino e a exibirá. Se a atividade não estiver na pilha de tarefas, uma nova será criada. Um ponto crucialmente importante a ser mencionado - o segundo parâmetro de PendingIntent.getActivity (), ou seja, requestCode deve ter um valor diferente de zero (eu até chamei de NON_ZERO_REQUEST_CODE no meu snippet), caso contrário, esses sinalizadores de intenção não funcionarão. Eu não tenho idéia do por que é como é. O sinalizador FLAG_ACTIVITY_SINGLE_TOP é intercambiável com o android: launchMode = "singleTop" na tag de atividade no manifesto.

Andrey Koretskyy
fonte
Lutando desde horas com essas bandeiras e queria saber por que mudar minhas bandeiras de intenção não mudou nada. Código de solicitação diferente de zero ... um ponto crucialmente importante a ser mencionado !!! Funciona agora! Obrigado!
Max Power
9

Eu sei que é antigo, mas nada acima foi adequado ao meu aplicativo.

Sem alterar manifestos e outras configurações, eis o código para trazer seu aplicativo de volta à frente - ou abri-lo quando estiver fechado

Intent notificationIntent = context.getPackageManager().getLaunchIntentForPackage(context.getPackageName());
notificationIntent.setPackage(null); // The golden row !!!
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
Raziza O
fonte
Esta é a verdadeira resposta!
azizbekian
2
não, não está funcionando - ele adiciona uma nova atividade em cima da atual.
Tobliug
3

Eu tentei isso e funcionou mesmo que o IDE estivesse reclamando do código

Intent notificationIntent = new Intent(THIS_CONTEXT, MainActivity.class);
    notificationIntent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
    PendingIntent intent = PendingIntent.getActivity(THIS_CONTEXT, 0, notificationIntent, Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);

    NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(THIS_CONTEXT)
            .setSmallIcon(R.drawable.cast_ic_notification_0)
            .setContentTitle("Title")
            .setContentText("Content")
            .setContentIntent(intent)
            .setPriority(PRIORITY_HIGH) //private static final PRIORITY_HIGH = 5;
            .setAutoCancel(true)
            /*.setDefaults(Notification.DEFAULT_SOUND | Notification.DEFAULT_VIBRATE | Notification.DEFAULT_LIGHTS)*/;
    NotificationManager mNotificationManager = (NotificationManager) THIS_CONTEXT.getSystemService(Context.NOTIFICATION_SERVICE);
    mNotificationManager.notify(0, mBuilder.build());
nada
fonte
2
<activity android: name = ". MainActivity" android: launchMode = "singleTask">
nada
Intent.FLAG_ACTIVITY_REORDER_TO_FRONTnão é um sinalizador válido paraPendingINtent.getActivity
rds 6/17
@rds Você pode vincular a fonte?
Louis CAD
@LouisCAD Lamento, eu não tenho acesso ao código-fonte mais
nada
@rds Não estou pedindo o código-fonte, mas o link de onde você lê que Intent.FLAG_ACTIVITY_REORDER_TO_FRONTnão é um sinalizador válido quando usado como um PendingIntent. Ou talvez você quis dizer que não é válido passar para o getActivitymétodo, mas é válido para usar como Intentsinalizador que é usado como um PendingIntent?
Louis CAD
2

Como você diz que deseja iniciar sua atividade se ela ainda não foi iniciada, talvez você não se importe em reiniciá-la. Também testei uma tonelada de sugestões e combinações de sinalizadores para a intenção, isso sempre trará a atividade que você precisa para a frente, embora não mantenha nenhum estado anteriormente associado a ela.

intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK|Intent.FLAG_ACTIVITY_NEW_TASK);

Somente API11 +.

Shogun Gordo
fonte
0

Eu tive um problema semelhante depois de adicionar as notificações encontradas no site de treinamento do Android . Nenhuma das outras respostas aqui funcionou para mim, no entanto, essa resposta funcionou para mim. Resumo:

final Intent notificationIntent = new Intent(context, YourActivity.class);
notificationIntent.setAction(Intent.ACTION_MAIN);
notificationIntent.addCategory(Intent.CATEGORY_LAUNCHER);
notificationIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
gattsbr
fonte