PendingIntent fonctionne correctement pour la première notification mais incorrectement pour le reste

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);
}

Cette fonction sera appelée plusieurs fois. Je voudrais que chacun notificationlance testActivity en cliquant dessus Malheureusement, seule la première notification lance testActivity. Cliquez sur le reste pour réduire la fenêtre de notification.

Informations supplémentaires: La fonction displayNotification()est dans une classe appelée UploadManager. Contextest passé à UploadManagerpartir du activityqui instancie. La fonction displayNotification()est appelée plusieurs fois à partir d'une fonction, également dans UploadManager, qui s'exécute dans un fichier AsyncTask.

Edit 1: J'ai oublié de mentionner que je passe la réponse String en Intent intenttant que 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);

Cela fait une grande différence car j'ai besoin de la "réponse" supplémentaire pour refléter la réponse String lorsque la notification a été créée. Au lieu de cela, en utilisant PendingIntent.FLAG_UPDATE_CURRENT, la "réponse" supplémentaire reflète la réponse String lors du dernier appel displayNotification().

Je sais pourquoi cela vient de la lecture de la documentation FLAG_UPDATE_CURRENT. Cependant, je ne sais pas comment contourner ce problème pour le moment.

Kapil Rajput
la source

Réponses:

125

Ne pas utiliser Intent.FLAG_ACTIVITY_NEW_TASKpour PendingIntent.getActivity, utilisez plutôt FLAG_ONE_SHOT


Copié à partir des commentaires:

Ensuite, définissez une action factice sur l'intention, sinon les extras sont supprimés. Par exemple

intent.setAction(Long.toString(System.currentTimeMillis()))
ognian
la source
Ce drapeau n'a pas fonctionné non plus pour la même raison, je pense, que mon extra ne fonctionne pas correctement (vérifiez mon Edit 1).
32
Ensuite, définissez une action factice sur l'intention, sinon les extras sont supprimés. Par exemple intent.setAction ("foo")
ognian
20
Excellent. Ça a marché. J'ai setAction (Long.toString (System.currentTimeMillis ())) en conjonction avec l'utilisation de FLAG_UPDATE_CURRENT que mbauer a suggéré. L'utilisation de FLAG_ONE_SHOT ne m'a permis de cliquer qu'une seule fois sur la notification (ce qui est logique). Merci beaucoup ognian.
5
"Ensuite, définissez une action factice sur l'intention, sinon les extras sont supprimés" - est-ce documenté quelque part?
Mr_and_Mrs_D
Le mécanisme setAction a fonctionné pour moi. Pour autant que documenté, pas sûr, mais la source pour Android est disponible sur android.googlesource.com ;-)
Norman H
62

J'avais du mal avec RemoteViewset plusieurs différents Intentspour chacun Buttonsur HomeScreenWidget. A travaillé une fois ajoutés:

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
la source
+1 Cool Merci. Une idée de pourquoi l'ajout d'intent.setAction () l'a fait fonctionner?
AjOnFire
setAction fonctionne mais que se passe-t-il si je dois réellement définir mes intentions Action sur autre chose? Pourquoi le framework est-il si bogué?
né le
2
J'adore la façon dont le SDK Android est si intuitif pour les développeurs ... (: ♥ ️ BTW lisez la réponse @ObjectiveTruth ci-dessous pour une explication sur la raison desetAction
Aviel Gross
1
Obtenait un comportement étrange, sans la méthode setAction, les extras d'intention fonctionneraient pendant le débogage, mais lorsqu'ils ne débogueraient pas, les extras d'intention seraient toujours les mêmes que les extras initiaux transmis lors du premier appel. J'ai constaté que lors du débogage, onCreate était toujours appelé lors de la navigation hors de l'application, mais sans débogage, onCreate n'était pas appelé, uniquement onStart. L'appel de la méthode setAction a résolu le problème, je suppose que c'est quelque chose à voir avec les intentions qui ne sont pas `` différentes '' si seule la valeur des extras a changé.
MaxJ
@clu Puisque j'utilise déjà setAction, ce que vous pouvez faire, c'est addCategory. PendingIntentutilise Intent.filterEqualspour vérifier l'égalité de l'action, des données, du type, de la classe et des catégories. developer.android.com/reference/android/content/…
iamreptar
43

Set Action Résolu cela pour moi. Voici ma compréhension de la situation:


J'ai plusieurs widgets qui ont un PendingIntent attaché à chacun. Chaque fois qu'un était mis à jour, ils étaient tous mis à jour. Les Flags sont là pour décrire ce qui se passe avec les PendingIntents qui sont exactement les mêmes.

La description de FLAG_UPDATE_CURRENT se lit beaucoup mieux maintenant:

Si le même PendingIntent que vous créez existe déjà, mettez à jour tous les anciens avec le nouveau PendingIntent que vous créez.

La définition d'exactement la même chose concerne l'ensemble de PendingIntent SAUF les extras. Ainsi, même si vous avez des extras différents pour chaque intention (pour moi, j'ajoutais appWidgetId), ils sont identiques pour Android.

L'ajout de .setAction avec une chaîne unique factice indique au système d'exploitation. Ceux-ci sont complètement différents et ne mettent rien à jour. En fin de compte, voici mon implémentation qui fonctionne comme je le voulais, où chaque widget a sa propre configuration Intent attachée:

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);

MISE À JOUR


Une solution encore meilleure au cas où vous travaillez avec des diffusions. Les PendingIntents uniques sont également définis par des codes de demande uniques. Voici ma solution:

//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);
ObjectiveVérité
la source
1
Nice Clean Solution
Varun Garg
1
Pour moi, avoir un identifiant unique et PendingIntent.FLAG_ONE_SHOT pour l'intention en attente, avec setAction sur l'intention a fonctionné.
Kaustuv le
il est un peu étrange de ne pas prendre en compte les modifications supplémentaires pour le changement d'intention, mais cela semble être vrai: /
zeroDivider
20

Je vois des réponses mais pas d'explications. De plus, aucune des réponses n'aborde toutes les solutions possibles, je vais donc essayer de clarifier cela.

Documentation:

Si vous avez vraiment besoin de plusieurs objets PendingIntent distincts actifs en même temps (par exemple pour les utiliser comme deux notifications qui sont toutes les deux affichées en même temps), vous devrez vous assurer qu'il y a quelque chose de différent à leur sujet pour les associer à des PendingIntents. Il peut s'agir de l'un des attributs Intent considérés par Intent.filterEquals, ou de différents entiers de code de requête fournis à getActivity (Context, int, Intent, int), getActivities (Context, int, Intent [], int), getBroadcast (Context, int , Intent, int) ou getService (Context, int, Intent, int).

Cause du problème:

Vous créez 2 notifications avec 2 intentions en attente. Chaque intention en attente est associée à une intention:

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

Cependant, ces 2 intentions sont égales, par conséquent, lorsque votre deuxième notification arrive, elle lancera la première intention.

Solution:

Vous devez rendre chaque intention unique, afin qu'aucune intention en attente ne soit jamais égale. Comment rendre les intentions uniques? Pas par les extras que vous mettez putExtra(). Même si les extras sont différents, les intentions peuvent toujours être égales. Pour rendre chaque intention unique, vous devez définir une valeur unique pour l'action d'intention, ou les données, ou le type, ou la classe, ou la catégorie, ou le code de demande: (l'un d'entre eux fonctionnera)

  • action: intent.setAction(...)
  • Les données: intent.setData(...)
  • type: intent.setType(...)
  • classe: intent.setClass(...)
  • Catégorie: intent.addCategory(...)
  • code requis: PendingIntent.getActivity(context, YOUR_UNIQUE_CODE, intent, Intent.FLAG_ONE_SHOT);

Remarque : la définition d'un code de requête unique peut être délicate car vous avez besoin d'un int, tandis que System.currentTimeMillis()renvoie long, ce qui signifie que certains chiffres seront supprimés. Par conséquent, je recommanderais soit d'aller avec la catégorie ou l' action et de définir une chaîne unique.

steliosf
la source
C'est ce qui a finalement fonctionné pour moi, en utilisant un identifiant unique pour chaque notification (nécessaire de toute façon pour l'annulabilité) et une catégorie personnalisée par action (n'aura jamais plusieurs actions du même type sur la même notification).
MandisaW
Oui, j'utilise également une catégorie unique pour chaque intention, cela fonctionne très bien.
steliosf
J'ai le même problème. deux notifications déclenchées en même temps. lorsque je clique sur la deuxième notification, rien ne se passe. après avoir défini cette setAction (Long.toString (System.currentTimeMillis ())); . son travail comme le charme. merci pour la belle explication @MScott
Anantha Babu
13

J'ai eu le même problème et j'ai pu le résoudre en changeant le drapeau en:

PendingIntent contentIntent = PendingIntent.getActivity(context, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
mbauer14
la source
Merci beaucoup d'avoir pris le temps de publier ce qui a résolu le problème pour vous. J'ai oublié de mentionner que je passe un extra dans l'intention. Cela rend le problème un peu plus complexe. Check my Edit 1.
9

Comme indiqué dans la documentation, utilisez un code de demande unique:

Si vous avez vraiment besoin de plusieurs objets PendingIntent distincts actifs en même temps (par exemple pour les utiliser comme deux notifications qui sont toutes les deux affichées en même temps), vous devrez vous assurer qu'il y a quelque chose de différent à leur sujet pour les associer à des PendingIntents. Il peut s'agir de l'un des attributs Intent considérés par Intent.filterEquals, ou de différents entiers de code de requête fournis à getActivity (Context, int, Intent, int), getActivities (Context, int, Intent [], int), getBroadcast (Context, int , Intent, int) ou getService (Context, int, Intent, int).

Tomasz
la source
1
C'est la seule réponse vraie et juste à la question. Je cherchais ça, parce que je voulais poster la même chose. :-)
Sevastyan Savanyuk
7

Fwiw, j'ai eu plus de chance avec PendingIntent.FLAG_CANCEL_CURRENTqu'avec PendingIntent.FLAG_UPDATE_CURRENT.

Jon Shemitz
la source
Je suis complètement d'accord avec ça. Il n'est pas nécessaire de remplir les intentions avec des extras inutiles si nous pouvons le faire annuler l'ancien et en créer un nouveau. Il est vrai que cela peut parfois être inutile si rien n'a changé, mais maintenant la question est "d'économiser de la mémoire ou de gagner du temps".
zeroDivider
4

J'ai eu le même problème et je l'ai résolu par les étapes ci-dessous

1) Effacez n'importe quel indicateur d'intention

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

2) insérez intent.setAction par le code ci-dessous

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

3) pour Pendingintent, insérez le code ci-dessous

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

J'espère travailler avec toi

Waleed A. Elgalil
la source
1
N'importe qui veut expliquer pourquoi cette réponse a été rejetée. Cela a fonctionné pour moi. Je ne sais pas si c'est une réponse légitime, mais cette solution est la solution parfaite. Au moins pour moi.
Sandeep R
A travaillé pour moi aussi! Merci!
Andres
2
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, Intent.FLAG_ACTIVITY_NEW_TASK);

Dans PendingIntent se trouvent deux paramètres int, le second et le dernier. Le deuxième est "code de demande" et il doit s'agir d'un numéro unicue (par exemple l'ID de votre notification), sinon si (comme dans votre exemple, il vaut zéro, il sera toujours écrasé).

Dmytro Ubogyi
la source
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
la source
0

pour envoyer des données plus correctement, vous devez envoyer avec l'intention en attente l'id de notification comme ceci: PendingIntent pendingIntent = PendingIntent.getActivity (context, (int) System.currentTimeMillis () , intent, PendingIntent.FLAG_UPDATE_CURRENT);

Amal Kronz
la source
0

J'ai le même problème et j'utilise PendingIntent.html.FLAG_UPDATE_CURRENT pour le résoudre.

J'ai vérifié le code source. Dans ActivityManagerService.java , la méthode clé est la suivante. Lorsque l'indicateur est PendingIntent.FLAG_UPDATE_CURRENT et que updateCurrent est true. Certains extras seront remplacés par de nouveaux et nous obtiendrons un PendingIntent remplacé.

    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
la source
-5

J'ai eu le même problème et j'ai pu le résoudre en changeant le drapeau en:

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
la source
4
Cela n'a rien à voir avec une question posée.
Paul Turchenko
Besoin de clarifier comment cela se rapporte à cette question.
Norman H