PendingIntent funziona correttamente per la prima notifica ma in modo errato per il 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);
}

Questa funzione verrà chiamata più volte. Vorrei che ciascuno notificationavvii testActivity quando cliccato. Sfortunatamente, solo la prima notifica avvia testActivity. Facendo clic sul resto, la finestra di notifica viene ridotta a icona.

Ulteriori informazioni: la funzione displayNotification()è in una classe chiamata UploadManager. Contextè passato UploadManagerda activityciò che istanzia. La funzione displayNotification()viene chiamata più volte da una funzione, anche in UploadManager, in esecuzione in un file AsyncTask.

Modifica 1: ho dimenticato di dire che sto passando la risposta String in Intent intentun file 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);

Ciò fa una grande differenza perché ho bisogno della "risposta" aggiuntiva per riflettere quale era la risposta della stringa quando è stata creata la notifica. Invece, utilizzando PendingIntent.FLAG_UPDATE_CURRENT, la "risposta" aggiuntiva riflette quale risposta String era nell'ultima chiamata a displayNotification().

So perché questo deriva dalla lettura della documentazione FLAG_UPDATE_CURRENT. Tuttavia, al momento non sono sicuro di come aggirarlo.

Risposte:


125

Non utilizzare Intent.FLAG_ACTIVITY_NEW_TASKper PendingIntent.getActivity, utilizza invece FLAG_ONE_SHOT


Copiato dai commenti:

Quindi imposta un'azione fittizia sull'intento, altrimenti gli extra vengono eliminati. Per esempio

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

Questo flag in realtà non ha funzionato per lo stesso motivo, penso, che il mio extra non funziona correttamente (controlla la mia Modifica 1).

32
Quindi imposta un'azione fittizia sull'intento, altrimenti gli extra vengono eliminati. Ad esempio intent.setAction ("foo")
ognian

20
Eccellente. Ha funzionato. Ho impostatoAction (Long.toString (System.currentTimeMillis ())) insieme all'utilizzo di FLAG_UPDATE_CURRENT suggerito da mbauer. L'utilizzo di FLAG_ONE_SHOT mi ha consentito di fare clic sulla notifica solo una volta (il che ha senso). Grazie mille ognian.

5
"Quindi imposta un'azione fittizia sull'Intent, altrimenti gli extra vengono eliminati" - è documentato da qualche parte?
Mr_and_Mrs_D

Il meccanismo setAction ha funzionato per me. Per quanto documentato, non sono sicuro, ma la fonte per Android è disponibile su android.googlesource.com ;-)
Norman H

62

Stavo lottando con RemoteViewse diversi diversi Intentsper ciascuno Buttonsu HomeScreenWidget. Ha funzionato quando aggiunto questi:

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

+1 Fantastico Grazie. Qualche idea sul perché l'aggiunta di intent.setAction () lo abbia fatto funzionare?
AjOnFire

setAction funziona ma cosa succede se devo impostare i miei intents Action su qualcos'altro? Perché il framework è così difettoso?
b

2
Adoro il modo in cui Android SDK è così intuitivo per gli sviluppatori ... (: ♥ ️ BTW leggi la risposta @ObjectiveTruth di seguito per una spiegazione sul motivosetAction
Aviel Gross

1
Stava ottenendo un comportamento strano, senza la chiamata al metodo setAction gli extra di intenti funzionerebbero durante il debug, ma quando non si esegue il debug gli extra di intento sarebbero sempre gli stessi degli extra iniziali passati nella prima chiamata. Ho scoperto che durante il debug, onCreate veniva sempre chiamato durante la navigazione fuori dall'app, ma mentre non eseguiva il debug, onCreate non veniva chiamato, solo onStart. La chiamata al metodo setAction ha risolto il problema, immagino che sia qualcosa a che fare con gli intenti che non sono "diversi" se solo il valore degli extra è cambiato.
MaxJ

@clu Dato che sto già usando setAction, quello che puoi fare è addCategory. PendingIntentutilizza Intent.filterEqualsper verificare l'uguaglianza di azione, dati, tipo, classe e categorie. developer.android.com/reference/android/content/…
iamreptar

43

Imposta azione Risolto questo problema per me. Ecco la mia comprensione della situazione:


Ho più widget a cui è associato un PendingIntent. Ogni volta che uno veniva aggiornato, tutti venivano aggiornati. Le bandiere sono lì per descrivere cosa succede con PendingIntents che sono esattamente gli stessi.

La descrizione FLAG_UPDATE_CURRENT ora si legge molto meglio:

Se lo stesso PendingIntent che stai creando esiste già, aggiorna tutti i vecchi con il nuovo PendingIntent che stai creando.

La definizione di esattamente lo stesso guarda all'intero PendingIntent TRANNE gli extra. Quindi, anche se hai diversi extra per ogni intento (per me stavo aggiungendo l'appWidgetId) ad Android, sono gli stessi.

L'aggiunta di .setAction con una stringa univoca fittizia comunica al sistema operativo. Questi sono completamente diversi e non aggiornano nulla. Alla fine ecco la mia implementazione che funziona come volevo, dove ogni Widget ha il suo Intento di configurazione allegato:

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

AGGIORNARE


Soluzione ancora migliore nel caso tu stia lavorando con le trasmissioni. Gli Unique PendingIntents sono definiti anche da codici di richiesta univoci. Ecco la mia soluzione:

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

1
Bella soluzione pulita
Varun Garg

1
Per me avere un ID univoco e PendingIntent.FLAG_ONE_SHOT per l'intento in sospeso, insieme a setAction sull'intento ha funzionato.
Kaustuv

è un po 'strano come non stia considerando modifiche extra per il cambio di intento, ma sembra essere vero: /
zeroDivider

20

Vedo risposte ma nessuna spiegazione. Inoltre nessuna delle risposte affronta tutte le possibili soluzioni, quindi cercherò di chiarirlo.

Documentazione:

Se hai davvero bisogno di più oggetti PendingIntent distinti attivi allo stesso tempo (ad esempio da usare come due notifiche che vengono mostrate entrambe contemporaneamente), allora dovrai assicurarti che ci sia qualcosa di diverso su di loro per associarli a PendingIntents. Può essere uno qualsiasi degli attributi Intent considerati da Intent.filterEquals o diversi numeri interi del codice di richiesta forniti a getActivity (Context, int, Intent, int), getActivities (Context, int, Intent [], int), getBroadcast (Context, int , Intent, int) o getService (Context, int, Intent, int).

Causa del problema:

Crei 2 notifiche con 2 intenti in sospeso. Ogni intento in sospeso è associato a un intento:

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

Tuttavia, questi 2 intenti sono uguali, quindi quando arriva la tua seconda notifica lancerà il primo intento.

Soluzione:

Devi rendere unico ogni intento, in modo che nessun intento in sospeso sarà mai uguale. Come rendi unici gli intenti? Non dagli extra con cui ti metti putExtra(). Anche se gli extra sono diversi, gli intenti potrebbero essere uguali. Per rendere unico ogni intento, devi impostare un valore univoco per l'azione, i dati, il tipo, la classe o la categoria o il codice di richiesta: (uno qualsiasi di questi funzionerà)

  • azione: intent.setAction(...)
  • dati: intent.setData(...)
  • genere: intent.setType(...)
  • classe: intent.setClass(...)
  • categoria: intent.addCategory(...)
  • codice richiesto: PendingIntent.getActivity(context, YOUR_UNIQUE_CODE, intent, Intent.FLAG_ONE_SHOT);

Nota : impostare un codice di richiesta univoco potrebbe essere complicato perché è necessario un int, mentre System.currentTimeMillis()restituisce long, il che significa che alcune cifre verranno rimosse. Pertanto consiglierei di andare con la categoria o l' azione e di impostare una stringa univoca.


Questo è ciò che alla fine ha funzionato per me, utilizzando un ID univoco per ogni notifica (necessario comunque per la cancellabilità) e una categoria personalizzata per azione (non avrà mai più azioni dello stesso tipo sulla stessa notifica).
MandisaW

Sì, sto usando anche una categoria unica per ogni intento, funziona alla grande.
steliosf

Ho lo stesso problema. due notifiche attivate contemporaneamente. quando faccio clic sulla seconda notifica non è successo nulla. dopo aver impostato questo setAction (Long.toString (System.currentTimeMillis ())); . funziona come un fascino. grazie per la bella spiegazione @MScott
Anantha Babu

13

Ho avuto lo stesso problema e sono stato in grado di risolverlo cambiando la bandiera in:

PendingIntent contentIntent = PendingIntent.getActivity(context, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);

Grazie mille per aver dedicato del tempo a pubblicare ciò che ha risolto il problema per te. Ho dimenticato di dire che sto passando un extra nell'Intent. Ciò rende il problema un po 'più complesso. Controlla la mia modifica 1.

9

Come dice la documentazione, utilizzare un codice di richiesta univoco:

Se hai davvero bisogno di più oggetti PendingIntent distinti attivi allo stesso tempo (ad esempio da usare come due notifiche che vengono mostrate entrambe contemporaneamente), allora dovrai assicurarti che ci sia qualcosa di diverso su di loro per associarli a PendingIntents. Può essere uno qualsiasi degli attributi Intent considerati da Intent.filterEquals o diversi numeri interi del codice di richiesta forniti a getActivity (Context, int, Intent, int), getActivities (Context, int, Intent [], int), getBroadcast (Context, int , Intent, int) o getService (Context, int, Intent, int).


1
Questa è l'unica risposta vera e giusta al punto. Lo stavo cercando, perché volevo pubblicare la stessa cosa. :-)
Sevastyan Savanyuk

7

Fwiw, ho avuto più fortuna con PendingIntent.FLAG_CANCEL_CURRENTche con PendingIntent.FLAG_UPDATE_CURRENT.


Sono totalmente d'accordo con questo. Non è necessario riempire gli intenti con extra inutili se possiamo fare in modo che cancelli quello vecchio e poi ne crei uno nuovo. È vero che a volte può essere inutile se non cambia nulla, ma ora la domanda è "risparmiare memoria o risparmiare tempo".
zeroDivider

4

Ho avuto lo stesso problema e l'ho risolto seguendo i passaggi seguenti

1) Cancella qualsiasi bandiera per intento

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

2) inserisci intent.setAction dal codice sottostante

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

3) per Pendingintent, inserire il codice sottostante

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

Spero di lavorare con te


1
A nessuno interessa spiegare perché questa risposta è stata sottovalutata. Questo ha funzionato per me. Non so se questa sia una risposta legittima, ma questa soluzione è la soluzione perfetta. Almeno per me.
Sandeep R

Ha funzionato anche per me! Grazie!
Andres

2
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, Intent.FLAG_ACTIVITY_NEW_TASK);

In PendingIntent ci sono due parametri int, il secondo e l'ultimo. Il secondo è "codice richiesta" e deve essere un numero unico (ad esempio l'ID della tua notifica), altrimenti se (come nel tuo esempio è uguale a zero, verrà sempre sovrascritto).


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

0

per inviare i dati in modo più corretto dovresti inviare con intento in sospeso l'ID di notifica in questo modo: PendingIntent pendingIntent = PendingIntent.getActivity (context, (int) System.currentTimeMillis () , intent, PendingIntent.FLAG_UPDATE_CURRENT);


0

Ho lo stesso problema e utilizzo PendingIntent.html.FLAG_UPDATE_CURRENT per risolverlo.

Ho controllato il codice sorgente. In ActivityManagerService.java , il metodo chiave è il seguente. Quando il flag è PendingIntent.FLAG_UPDATE_CURRENT e updateCurrent è true. Alcuni extra verranno sostituiti da nuovi e otterremo un PendingIntent sostituito.

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


-5

Ho avuto lo stesso problema e sono stato in grado di risolverlo cambiando la bandiera in:

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

    }

4
Non ha nulla a che fare con una domanda posta.
Paul Turchenko

È necessario chiarire come si collega a questa domanda.
Norman H
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.