PendingIntent funciona corretamente para a primeira notificação, mas incorretamente para o resto

87
  protected void displayNotification(String response) {
    Intent intent = new Intent(context, testActivity.class);
    PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, Intent.FLAG_ACTIVITY_NEW_TASK);

    Notification notification = new Notification(R.drawable.icon, "Upload Started", System.currentTimeMillis());
    notification.setLatestEventInfo(context, "Upload", response, pendingIntent);

    nManager.notify((int)System.currentTimeMillis(), notification);
}

Esta função será chamada várias vezes. Gostaria que cada um notificationiniciasse testActivity quando clicado. Infelizmente, apenas a primeira notificação inicia testActivity. Clicar no resto faz com que a janela de notificação seja minimizada.

Informações extras: a função displayNotification()está em uma classe chamada UploadManager. Contexté passado para a UploadManagerpartir das activityinstancias. A função displayNotification()é chamada várias vezes a partir de uma função, também em UploadManager, que está sendo executada em um AsyncTask.

Edição 1: esqueci de mencionar que estou passando a resposta de String Intent intentcomo um extra.

  protected void displayNotification(String response) {
    Intent intent = new Intent(context, testActivity.class);
    intent.putExtra("response", response);
    PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);

Isso faz uma grande diferença porque eu preciso da "resposta" extra para refletir qual era a resposta da String quando a notificação foi criada. Em vez disso, usando PendingIntent.FLAG_UPDATE_CURRENT, a "resposta" extra reflete a resposta da String na última chamada displayNotification().

Eu sei por que isso acontece lendo a documentação no FLAG_UPDATE_CURRENT. No entanto, não tenho certeza de como contornar isso no momento.

Kapil Rajput
fonte

Respostas:

124

Não use Intent.FLAG_ACTIVITY_NEW_TASKpara PendingIntent.getActivity, use FLAG_ONE_SHOT em vez disso


Copiado dos comentários:

Em seguida, defina alguma ação fictícia no Intent, caso contrário, os extras serão descartados. Por exemplo

intent.setAction(Long.toString(System.currentTimeMillis()))
Ognian
fonte
Este sinalizador também não funcionou pela mesma razão, eu acho, que meu extra não está funcionando corretamente (verifique minha edição 1).
32
Em seguida, defina alguma ação fictícia no Intent, caso contrário, os extras serão descartados. Por exemplo intent.setAction ("foo")
ognian
20
Excelente. Isso funcionou. Eu setAction (Long.toString (System.currentTimeMillis ())) em conjunto com o uso de FLAG_UPDATE_CURRENT que mbauer sugeriu. Usar FLAG_ONE_SHOT só me permitiu clicar na notificação uma vez (o que faz sentido). Muito obrigado, ognian.
5
"Em seguida, defina alguma ação fictícia no Intent, caso contrário, os extras serão descartados" - isso está documentado em algum lugar?
Mr_and_Mrs_D
O mecanismo setAction funcionou para mim. Tanto quanto documentado, não tenho certeza, mas a fonte do Android está disponível em android.googlesource.com ;-)
Norman H
62

Estava lutando com RemoteViewse vários diferentes Intentspara cada um Buttonno HomeScreenWidget. Funcionou quando adicionado estes:

1 intent.setAction(Long.toString(System.currentTimeMillis()));

2 PendingIntent.FLAG_UPDATE_CURRENT

        PackageManager pm = context.getPackageManager();

        Intent intent = new Intent(context, MyOwnActivity.class);
        intent.putExtra("foo_bar_extra_key", "foo_bar_extra_value");
        intent.setAction(Long.toString(System.currentTimeMillis()));
        PendingIntent pendingIntent = PendingIntent.getActivity(context, 0,
                intent, PendingIntent.FLAG_UPDATE_CURRENT);
        RemoteViews views = new RemoteViews(context.getPackageName(),
                R.layout.widget_layout);
        views.setOnClickPendingIntent(my_button_r_id_received_in_parameter, pendingIntent);
ViliusK
fonte
+1 Legal, obrigado. Alguma ideia de por que adicionar intent.setAction () fez com que funcionasse?
AjOnFire,
setAction funciona, mas e se eu realmente precisar definir minha ação intents para outra coisa? Por que o framework é tão problemático?
shoke de
2
Eu adoro como o Android SDK é tão intuitivo para os desenvolvedores ... (: ♥ ️ A propósito, leia a resposta @ObjectiveTruth abaixo para uma explicação sobre o motivo desetAction
Aviel Gross
1
Estava obtendo um comportamento estranho, sem o método setAction, extras de intent de chamada funcionariam durante a depuração, mas quando não fosse depurado, os extras de intent seriam sempre os mesmos que os extras iniciais passados ​​na primeira chamada. Descobri que durante a depuração, onCreate era sempre chamado ao navegar fora do aplicativo, mas embora não estivesse depurando, onCreate não era chamado, apenas onStart. Chamar o método setAction resolveu o problema, estou supondo que é algo a ver com intents não serem 'diferentes' se apenas o valor dos extras mudou.
MaxJ
@clu Como já estou usando setAction, o que você pode fazer é addCategory. PendingIntentusa Intent.filterEqualspara verificar a igualdade da ação, dados, tipo, classe e categorias. developer.android.com/reference/android/content/…
iamreptar
43

Definir ação resolveu isso para mim. Este é meu entendimento da situação:


Eu tenho vários widgets que têm um PendingIntent anexado a cada um. Sempre que um era atualizado, todos eram atualizados. Os sinalizadores existem para descrever o que acontece com os PendingIntents que são exatamente iguais.

A descrição de FLAG_UPDATE_CURRENT é muito melhor agora:

Se o mesmo PendingIntent que você está criando já existe, atualize todos os antigos para o novo PendingIntent que está criando.

A definição de exatamente o mesmo considera todo o PendingIntent, EXCETO os extras. Portanto, mesmo que você tenha extras diferentes em cada intent (para mim, eu estava adicionando o appWidgetId) ao android, eles são iguais.

Adicionar .setAction com alguma string única fictícia informa o sistema operacional. Eles são completamente diferentes e não atualizam nada. No final, aqui está minha implementação que funciona como eu queria, onde cada widget tem seu próprio Intent de configuração anexado:

Intent configureIntent = new Intent(context, ActivityPreferences.class);

configureIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);

configureIntent.setAction("dummy_unique_action_identifyer" + appWidgetId);

PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, configureIntent,
    PendingIntent.FLAG_UPDATE_CURRENT);

ATUALIZAR


Solução ainda melhor caso você esteja trabalhando com transmissões. Unique PendingIntents também são definidos por códigos de solicitação exclusivos. Esta é minha solução:

//Weee, magic number, just want it to be positive nextInt(int r) means between 0 and r
int dummyuniqueInt = new Random().nextInt(543254); 
PendingIntent pendingClearScreenIntent = PendingIntent.getBroadcast(context, 
    dummyuniqueInt, clearScreenIntent, PendingIntent.FLAG_UPDATE_CURRENT);
ObjectiveTruth
fonte
1
Nice Clean Solution
Varun Garg
1
Para mim, ter id exclusivo e PendingIntent.FLAG_ONE_SHOT para intent pendente, junto com setAction no intent funcionou.
Kaustuv
é um pouco estranho como não está considerando mudanças extras para a mudança de intenção, mas parece ser verdade: /
zeroDivider
19

Eu vejo respostas, mas nenhuma explicação. Além disso, nenhuma das respostas aborda todas as soluções possíveis, então tentarei deixar isso claro.

Documentação:

Se você realmente precisa de vários objetos PendingIntent distintos ativos ao mesmo tempo (como para usar como duas notificações que são mostradas ao mesmo tempo), você precisará garantir que haja algo diferente sobre eles para associá-los a diferentes PendingIntents. Pode ser qualquer um dos atributos Intent considerados por Intent.filterEquals ou diferentes números inteiros de código de solicitação fornecidos para getActivity (Context, int, Intent, int), getActivities (Context, int, Intent [], int), getBroadcast (Context, int , Intent, int) ou getService (Context, int, Intent, int).

Causa do problema:

Você cria 2 notificações com 2 intents pendentes. Cada intent pendente está associado a um intent:

Intent intent = new Intent(context, testActivity.class);

No entanto, essas 2 intents são iguais, portanto, quando sua segunda notificação chegar, ela iniciará a primeira intent.

Solução:

Você deve tornar cada intent única, para que nenhuma intent pendente seja igual. Como você torna as intenções únicas? Não pelos extras que você usa putExtra(). Mesmo que os extras sejam diferentes, as intenções ainda podem ser iguais. Para tornar cada intent única, você deve definir um valor único para a ação da intent, ou dados, ou tipo, ou classe, ou categoria, ou código de solicitação: (qualquer um deles funcionará)

  • açao: intent.setAction(...)
  • dados: intent.setData(...)
  • tipo: intent.setType(...)
  • classe: intent.setClass(...)
  • categoria: intent.addCategory(...)
  • solicitar código: PendingIntent.getActivity(context, YOUR_UNIQUE_CODE, intent, Intent.FLAG_ONE_SHOT);

Observação : definir um código de solicitação exclusivo pode ser complicado porque você precisa de um int, enquanto System.currentTimeMillis()retorna longo, o que significa que alguns dígitos serão removidos. Portanto, eu recomendaria ir com a categoria ou ação e definir uma string única.

Steliosf
fonte
Isso é o que funcionou para mim, usando uma ID exclusiva para cada notificação (necessária de qualquer forma para cancelabilidade) e uma categoria personalizada por ação (nunca terá várias ações do mesmo tipo na mesma notificação).
MandisaW
Sim, estou usando uma categoria única para cada intent também, funciona muito bem.
steliosf
Tenho o mesmo problema. duas notificações acionadas ao mesmo tempo. quando clico na segunda notificação, nada aconteceu. depois de definir este setAction (Long.toString (System.currentTimeMillis ())); . está funcionando como charme. obrigado pela bela explicação @MScott
Anantha Babu
13

Tive o mesmo problema e consegui corrigi-lo alterando o sinalizador para:

PendingIntent contentIntent = PendingIntent.getActivity(context, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
mbauer14
fonte
Muito obrigado por postar o que corrigiu o problema para você. Esqueci de mencionar que estou passando um extra para o Intent. Isso torna o problema um pouco mais complexo. Verifique minha edição 1.
9

Conforme a documentação, use um código de solicitação exclusivo:

Se você realmente precisa de vários objetos PendingIntent distintos ativos ao mesmo tempo (como para usar como duas notificações que são mostradas ao mesmo tempo), você precisará garantir que haja algo diferente sobre eles para associá-los a diferentes PendingIntents. Pode ser qualquer um dos atributos Intent considerados por Intent.filterEquals ou diferentes números inteiros de código de solicitação fornecidos para getActivity (Context, int, Intent, int), getActivities (Context, int, Intent [], int), getBroadcast (Context, int , Intent, int) ou getService (Context, int, Intent, int).

Tomasz
fonte
1
Esta é a única resposta verdadeira e certa. Estava procurando, pois queria postar a mesma coisa. :-)
Sevastyan Savanyuk
7

Fwiw, tive mais sorte com do PendingIntent.FLAG_CANCEL_CURRENTque com PendingIntent.FLAG_UPDATE_CURRENT.

Jon Shemitz
fonte
Concordo completamente com isto. Não há necessidade de preencher intents com extras inúteis se pudermos fazer com que cancele o antigo e, em seguida, criar um novo. É verdade que às vezes pode ser inútil se nada mudou, mas agora a questão é "economizar memória ou tempo".
zeroDivider
4

Eu tive o mesmo problema e corrigi-lo pelas etapas abaixo

1) Limpe qualquer sinalizador de intenção

intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);

2) insira intent.setAction pelo código abaixo

 intent.setAction(Long.toString(System.currentTimeMillis()));

3) para Pendingintent, insira o código abaixo

   PendingIntent Pintent = PendingIntent.getActivity(ctx,0, intent,PendingIntent.FLAG_UPDATE_CURRENT);

Espero trabalhar com você

Waleed A. Elgalil
fonte
1
Qualquer um pode explicar por que essa resposta foi rejeitada. Isso funcionou para mim. Não sei se esta é uma resposta legítima, mas esta solução é a solução perfeita. Pelo menos para mim.
Sandeep R
Funcionou para mim também! Obrigado!
Andres
2
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, Intent.FLAG_ACTIVITY_NEW_TASK);

Em PendingIntent há dois parâmetros int, o segundo e o último. O segundo é o "código de solicitação" e deve ser um número único (por exemplo, id de sua notificação), senão se (como em seu exemplo for igual a zero, sempre será sobrescrito).

Dmytro Ubogyi
fonte
0
// Use pending Intent and  also use unique id for display notification....
// Get a PendingIntent containing the entire back stack
PendingIntent notificationPendingIntent = stackBuilder.getPendingIntent(0,PendingIntent.FLAG_UPDATE_CURRENT);
NotificationManager mNotificationManager = (NotificationManager)  sqlitewraper.context.getSystemService(Context.NOTIFICATION_SERVICE);
// Issue the notification
mNotificationManager.notify(id, builder.build());
MIkka Marmik
fonte
0

para enviar dados extras corretamente, você deve enviar com intenção pendente o id de notificação como este: PendingIntent pendingIntent = PendingIntent.getActivity (context, (int) System.currentTimeMillis () , intent, PendingIntent.FLAG_UPDATE_CURRENT);

Amal Kronz
fonte
0

Eu tenho o mesmo problema e uso o PendingIntent.html.FLAG_UPDATE_CURRENT para corrigi-lo.

Eu verifiquei o código-fonte. Em ActivityManagerService.java , o método principal é o seguinte. Quando o sinalizador é PendingIntent.FLAG_UPDATE_CURRENT e updateCurrent é verdadeiro. Alguns extras serão substituídos por novos e obteremos um PendingIntent substituído.

    IIntentSender getIntentSenderLocked(int type, String packageName,
            int callingUid, int userId, IBinder token, String resultWho,
            int requestCode, Intent[] intents, String[] resolvedTypes, int flags,
            Bundle bOptions) {

// ... omitted

        final boolean noCreate = (flags&PendingIntent.FLAG_NO_CREATE) != 0;
        final boolean cancelCurrent = (flags&PendingIntent.FLAG_CANCEL_CURRENT) != 0;
        final boolean updateCurrent = (flags&PendingIntent.FLAG_UPDATE_CURRENT) != 0;
        flags &= ~(PendingIntent.FLAG_NO_CREATE|PendingIntent.FLAG_CANCEL_CURRENT
                |PendingIntent.FLAG_UPDATE_CURRENT);

        PendingIntentRecord.Key key = new PendingIntentRecord.Key(
                type, packageName, activity, resultWho,
                requestCode, intents, resolvedTypes, flags, bOptions, userId);
        WeakReference<PendingIntentRecord> ref;
        ref = mIntentSenderRecords.get(key);
        PendingIntentRecord rec = ref != null ? ref.get() : null;
        if (rec != null) {
            if (!cancelCurrent) {
                if (updateCurrent) {
                    if (rec.key.requestIntent != null) {
                        rec.key.requestIntent.replaceExtras(intents != null ?
                                intents[intents.length - 1] : null);
                    }
                    if (intents != null) {
                        intents[intents.length-1] = rec.key.requestIntent;
                        rec.key.allIntents = intents;
                        rec.key.allResolvedTypes = resolvedTypes;
                    } else {
                        rec.key.allIntents = null;
                        rec.key.allResolvedTypes = null;
                    }
                }
                return rec;
            }
            rec.canceled = true;
            mIntentSenderRecords.remove(key);
        }

qin hao
fonte
-5

Tive o mesmo problema e consegui corrigi-lo alterando o sinalizador para:

LayoutInflater factory = LayoutInflater.from(this);            
      final View textEntryView = factory.inflate(R.layout.appointment, null);
      AlertDialog.Builder bulider= new AlertDialog.Builder(PatientDetail.this);
      final AlertDialog alert=bulider.create();


        bulider.setTitle("Enter Date/Time");
        bulider.setView(textEntryView);
        bulider.setPositiveButton("Save", new DialogInterface.OnClickListener() {

                public void onClick(DialogInterface dialog, int which) {
                      EditText typeText=(EditText) textEntryView.findViewById(R.id.Editdate);
                      EditText input1 =(EditText) textEntryView.findViewById(R.id.Edittime);
                      getDateAndTime(typeText.getText().toString(),input1.getText().toString());
                }
            });
        bulider.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {

                public void onClick(DialogInterface dialog, int which) {
                    dialog.cancel();
                }
            });

        bulider.show();

    }
user1917789
fonte
4
Não tem nada a ver com uma pergunta feita.
Paul Turchenko de
Precisa esclarecer como isso se relaciona com esta questão.
Norman H