PendingIntent funktioniert für die erste Benachrichtigung korrekt, für den Rest jedoch falsch

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

Diese Funktion wird mehrmals aufgerufen. Ich möchte, dass jeder notificationtestActivity startet, wenn er angeklickt wird. Leider startet nur die erste Benachrichtigung testActivity. Wenn Sie auf den Rest klicken, wird das Benachrichtigungsfenster minimiert.

Zusätzliche Informationen: Die Funktion displayNotification()befindet sich in einer Klasse namens UploadManager. Contextwird UploadManagervon dem übergeben activity, der instanziiert. Die Funktion displayNotification()wird mehrmals von einer Funktion aufgerufen, auch in UploadManager, die in einem ausgeführt wird AsyncTask.

Edit 1: Ich habe vergessen zu erwähnen, dass ich die String-Antwort Intent intentals übergeben werde 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);

Dies macht einen großen Unterschied, da ich die zusätzliche "Antwort" benötige, um die String-Antwort bei der Erstellung der Benachrichtigung wiederzugeben. Stattdessen PendingIntent.FLAG_UPDATE_CURRENTspiegelt die zusätzliche "Antwort" bei Verwendung die Zeichenfolgenantwort beim letzten Aufruf von wider displayNotification().

Ich weiß, warum das so ist, wenn ich die Dokumentation weiter lese FLAG_UPDATE_CURRENT. Ich bin mir jedoch im Moment nicht sicher, wie ich das umgehen soll.

Kapil Rajput
quelle

Antworten:

123

Verwenden Sie nicht Intent.FLAG_ACTIVITY_NEW_TASKfür PendingIntent.getActivity, Verwendung FLAG_ONE_SHOT statt


Aus Kommentaren kopiert:

Stellen Sie dann eine Dummy-Aktion für die Absicht ein, andernfalls werden Extras gelöscht. Beispielsweise

intent.setAction(Long.toString(System.currentTimeMillis()))
ognian
quelle
Dieses Flag hat auch aus dem gleichen Grund nicht funktioniert, ich denke, dass mein Extra nicht richtig funktioniert (überprüfen Sie meine Bearbeitung 1).
32
Stellen Sie dann eine Dummy-Aktion für die Absicht ein, andernfalls werden Extras gelöscht. Zum Beispiel intent.setAction ("foo")
ognian
20
Ausgezeichnet. Das hat funktioniert. Ich setzeAction (Long.toString (System.currentTimeMillis ())) in Verbindung mit der von mbauer vorgeschlagenen Verwendung von FLAG_UPDATE_CURRENT. Mit FLAG_ONE_SHOT konnte ich nur einmal auf die Benachrichtigung klicken (was sinnvoll ist). Vielen Dank ognian.
5
"Dann setzen Sie eine Dummy-Aktion auf die Absicht, sonst werden Extras fallen gelassen" - ist das irgendwo dokumentiert?
Mr_and_Mrs_D
Der setAction-Mechanismus hat bei mir funktioniert. Soweit dokumentiert, nicht sicher, aber die Quelle für Android finden Sie unter android.googlesource.com ;-)
Norman H
62

Kämpfte mit RemoteViewsund mehrere verschiedene Intentsfür jedes Buttonauf HomeScreenWidget. Funktionierte beim Hinzufügen dieser:

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
quelle
+1 Cool Danke. Irgendeine Idee, warum das Hinzufügen von intent.setAction () funktioniert hat?
AjOnFire
setAction funktioniert, aber was ist, wenn ich meine Absichten Action auf etwas anderes setzen muss? Warum ist das Framework so fehlerhaft?
schüttelte
2
Ich finde es einfach toll, wie intuitiv Android SDK für Entwickler ist ... (: ♥ ️ Lesen Sie übrigens die Antwort auf @ObjectiveTruth unten, um den Grund fürsetAction
Aviel Gross
1
Wurde ein seltsames Verhalten festgestellt, ohne die setAction-Methode würden Call-Intent-Extras beim Debuggen funktionieren, aber wenn das Debuggen nicht durchgeführt wird, sind die Intent-Extras immer dieselben wie die ersten Extras, die beim ersten Aufruf übergeben wurden. Ich fand heraus, dass beim Debuggen onCreate immer aufgerufen wurde, wenn aus der App heraus navigiert wurde, aber während des Debuggens wurde onCreate nicht aufgerufen, sondern nur onStart. Das Aufrufen der setAction-Methode hat das Problem behoben. Ich vermute, es hat etwas damit zu tun, dass die Absichten nicht "anders" sind, wenn sich nur der Extras-Wert geändert hat.
MaxJ
@clu Da ich bereits benutze setAction, können Sie Folgendes tun addCategory. PendingIntentwird verwendet Intent.filterEquals, um die Gleichheit von Aktion, Daten, Typ, Klasse und Kategorien zu überprüfen. developer.android.com/reference/android/content/…
iamreptar
43

Aktion einstellen Hat das für mich gelöst. Hier ist mein Verständnis der Situation:


Ich habe mehrere Widgets, an die jeweils ein PendingIntent angehängt ist. Wann immer man aktualisiert wurde, wurden alle aktualisiert. Die Flags beschreiben, was mit genau gleichen PendingIntents passiert.

Die Beschreibung von FLAG_UPDATE_CURRENT liest sich jetzt viel besser:

Wenn derselbe PendingIntent, den Sie erstellen, bereits vorhanden ist, aktualisieren Sie alle alten auf den neuen PendingIntent, den Sie erstellen.

Die Definition von genau dem gleichen betrachtet das gesamte PendingIntent mit Ausnahme der Extras. Selbst wenn Sie für jede Absicht unterschiedliche Extras haben (für mich habe ich die appWidgetId hinzugefügt), sind sie für Android gleich.

Das Hinzufügen von .setAction mit einer eindeutigen Dummy-Zeichenfolge teilt dem Betriebssystem mit. Diese sind völlig anders und aktualisieren nichts. Am Ende ist hier meine Implementierung, die wie gewünscht funktioniert, wobei jedem Widget seine eigene Konfigurationsabsicht beigefügt ist:

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

AKTUALISIEREN


Noch bessere Lösung für den Fall, dass Sie mit Sendungen arbeiten. Eindeutige PendingIntents werden auch durch eindeutige Anforderungscodes definiert. Hier ist meine Lösung:

//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
quelle
1
Schöne saubere Lösung
Varun Garg
1
Für mich hat die eindeutige ID und PendingIntent.FLAG_ONE_SHOT für ausstehende Absichten zusammen mit setAction für Absichten funktioniert.
Kaustuv
Es ist ein bisschen seltsam, dass es keine zusätzlichen Änderungen für die Absichtsänderung berücksichtigt, aber es scheint wahr zu sein: /
zeroDivider
18

Ich sehe Antworten, aber keine Erklärungen. Außerdem spricht keine der Antworten alle möglichen Lösungen an, daher werde ich versuchen, dies klar zu machen.

Dokumentation:

Wenn Sie wirklich mehrere unterschiedliche PendingIntent-Objekte benötigen, die gleichzeitig aktiv sind (z. B. um zwei Benachrichtigungen zu verwenden, die beide gleichzeitig angezeigt werden), müssen Sie sicherstellen, dass sie etwas anderes haben, um sie mit verschiedenen zu verknüpfen PendingIntents. Dies kann eines der Intent-Attribute sein, die von Intent.filterEquals berücksichtigt werden, oder verschiedene Anforderungscode-Ganzzahlen, die für getActivity (Context, int, Intent, int), getActivities (Context, int, Intent [], int), getBroadcast (Context, int) angegeben werden , Intent, int) oder getService (Context, int, Intent, int).

Ursache des Problems:

Sie erstellen 2 Benachrichtigungen mit 2 ausstehenden Absichten. Jede ausstehende Absicht ist mit einer Absicht verbunden:

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

Diese beiden Absichten sind jedoch gleich. Wenn Ihre zweite Benachrichtigung eintrifft, wird die erste Absicht gestartet.

Lösung:

Sie müssen jede Absicht einzigartig machen, damit keine ausstehenden Absichten jemals gleich sind. Wie machen Sie die Absichten einzigartig? Nicht durch die Extras, die Sie mit setzen putExtra(). Selbst wenn die Extras unterschiedlich sind, können die Absichten immer noch gleich sein. Um jede Absicht eindeutig zu machen, müssen Sie einen eindeutigen Wert für die Absichtsaktion oder die Daten oder den Typ oder die Klasse oder die Kategorie oder den Anforderungscode festlegen: (einer dieser Codes funktioniert)

  • Aktion: intent.setAction(...)
  • Daten: intent.setData(...)
  • Art: intent.setType(...)
  • Klasse: intent.setClass(...)
  • Kategorie: intent.addCategory(...)
  • Anfrage Code: PendingIntent.getActivity(context, YOUR_UNIQUE_CODE, intent, Intent.FLAG_ONE_SHOT);

Hinweis : Das Festlegen eines eindeutigen Anforderungscodes kann schwierig sein, da Sie ein int benötigen, während System.currentTimeMillis()long zurückgegeben wird. Dies bedeutet, dass einige Ziffern entfernt werden. Daher würde ich empfehlen, entweder mit der Kategorie oder der Aktion zu gehen und eine eindeutige Zeichenfolge festzulegen.

steliosf
quelle
Dies hat letztendlich für mich funktioniert: Verwenden Sie für jede Benachrichtigung eine eindeutige ID (die ohnehin für die Stornierung erforderlich ist) und eine benutzerdefinierte Kategorie pro Aktion (es werden niemals mehrere Aktionen desselben Typs für dieselbe Benachrichtigung verwendet).
MandisaW
Ja, ich verwende auch eine eindeutige Kategorie für jede Absicht, es funktioniert großartig.
Steliosf
Habe das gleiche Problem. zwei Benachrichtigungen gleichzeitig ausgelöst. Wenn ich auf die zweite Benachrichtigung klicke, ist nichts passiert. nach dem Setzen dieser setAction (Long.toString (System.currentTimeMillis ())); . Es funktioniert wie Charme. Vielen Dank für die nette Erklärung @MScott
Anantha Babu
13

Ich hatte das gleiche Problem und konnte es beheben, indem ich das Flag auf änderte:

PendingIntent contentIntent = PendingIntent.getActivity(context, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
mbauer14
quelle
Vielen Dank, dass Sie sich die Zeit genommen haben, um zu veröffentlichen, was das Problem für Sie behoben hat. Ich habe vergessen zu erwähnen, dass ich ein Extra in die Absicht übergebe. Dies macht das Problem etwas komplexer. Überprüfen Sie meine Bearbeitung 1.
9

Verwenden Sie als Dokumentation den eindeutigen Anforderungscode:

Wenn Sie wirklich mehrere unterschiedliche PendingIntent-Objekte benötigen, die gleichzeitig aktiv sind (z. B. um zwei Benachrichtigungen zu verwenden, die beide gleichzeitig angezeigt werden), müssen Sie sicherstellen, dass sie etwas anderes haben, um sie mit verschiedenen zu verknüpfen PendingIntents. Dies kann eines der Intent-Attribute sein, die von Intent.filterEquals berücksichtigt werden, oder verschiedene Anforderungscode-Ganzzahlen, die für getActivity (Context, int, Intent, int), getActivities (Context, int, Intent [], int), getBroadcast (Context, int) angegeben werden , Intent, int) oder getService (Context, int, Intent, int).

Tomasz
quelle
1
Dies ist die einzig wahre und zutreffende Antwort. Ich habe danach gesucht, weil ich das Gleiche posten wollte. :-)
Sevastyan Savanyuk
7

Fwiw, ich hatte besseres Glück mit PendingIntent.FLAG_CANCEL_CURRENTals mit PendingIntent.FLAG_UPDATE_CURRENT.

Jon Shemitz
quelle
Dem stimme ich voll und ganz zu. Es ist nicht nötig, Absichten mit nutzlosen Extras zu füllen, wenn wir die alte stornieren und dann eine neue erstellen können. Es ist wahr, dass es manchmal nutzlos sein kann, wenn sich nichts ändert, aber jetzt lautet die Frage "Speicher sparen oder Zeit sparen".
zeroDivider
4

Ich hatte das gleiche Problem und habe es durch die folgenden Schritte behoben

1) Löschen Sie alle Flaggen für Absichten

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

2) Fügen Sie intent.setAction mit dem folgenden Code ein

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

3) Geben Sie für Pendingintent den folgenden Code ein

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

Ich hoffe mit dir zu arbeiten

Waleed A. Elgalil
quelle
1
Jeder möchte erklären, warum diese Antwort abgelehnt wurde. Das hat bei mir funktioniert. Ich weiß nicht, ob dies eine legitime Antwort ist, aber diese Lösung ist die perfekte Lösung. Zumindest für mich.
Sandeep R
Hat auch für mich gearbeitet! Vielen Dank!
Andres
2
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, Intent.FLAG_ACTIVITY_NEW_TASK);

In PendingIntent gibt es zwei int-Parameter, den zweiten und den letzten. Der zweite ist "Anforderungscode" und muss eine eindeutige Nummer sein (zum Beispiel die ID Ihrer Benachrichtigung), andernfalls wenn (wie in Ihrem Beispiel ist es gleich Null, wird es immer überschrieben).

Dmytro Ubogyi
quelle
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
quelle
0

Um Daten besonders korrekt zu senden, sollten Sie die Benachrichtigungs-ID mit ausstehender Absicht wie folgt senden: PendingIntent pendingIntent = PendingIntent.getActivity (Kontext, (int) System.currentTimeMillis () , Absicht, PendingIntent.FLAG_UPDATE_CURRENT);

Amal Kronz
quelle
0

Ich habe das gleiche Problem und verwende PendingIntent.html.FLAG_UPDATE_CURRENT , um es zu beheben.

Ich habe den Quellcode überprüft. In ActivityManagerService.java lautet die Schlüsselmethode wie folgt. Wenn das Flag PendingIntent.FLAG_UPDATE_CURRENT lautet und updateCurrent true ist. Einige Extras werden durch neue ersetzt und wir erhalten einen ersetzten PendingIntent.

    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
quelle
-5

Ich hatte das gleiche Problem und konnte es beheben, indem ich das Flag auf änderte:

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
quelle
4
Es hat nichts mit einer gestellten Frage zu tun.
Paul Turchenko
Es muss geklärt werden, wie es mit dieser Frage zusammenhängt.
Norman H