PendingIntent funciona correctamente para la primera notificación pero incorrectamente para el 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 función se llamará varias veces. Me gustaría que cada uno notificationiniciara testActivity cuando se hace clic en él. Desafortunadamente, solo la primera notificación inicia testActivity. Al hacer clic en el resto, la ventana de notificación se minimiza.

Información adicional: la función displayNotification()está en una clase llamada UploadManager. Contextse pasa UploadManagerdesde el activityque instancia. La función displayNotification()se llama varias veces desde una función, también en UploadManager, que se ejecuta en un AsyncTask.

Edición 1: Olvidé mencionar que estoy pasando la respuesta de cadena Intent intentcomo un 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);

Esto hace una gran diferencia porque necesito la "respuesta" adicional para reflejar cuál era la respuesta de cadena cuando se creó la notificación. En su lugar, el uso de PendingIntent.FLAG_UPDATE_CURRENTla "respuesta" adicional refleja a qué fue la respuesta de String en la última llamada displayNotification().

Sé por qué esto es por leer la documentación en FLAG_UPDATE_CURRENT. Sin embargo, no estoy seguro de cómo solucionarlo en este momento.

Kapil Rajput
fuente

Respuestas:

125

No lo use Intent.FLAG_ACTIVITY_NEW_TASKpara PendingIntent.getActivity, use FLAG_ONE_SHOT en su lugar


Copiado de comentarios:

Luego establezca alguna acción ficticia en el Intent, de lo contrario, se eliminan los extras. Por ejemplo

intent.setAction(Long.toString(System.currentTimeMillis()))
ognian
fuente
Esta bandera en realidad tampoco funcionó por la misma razón, creo, que mi extra no funciona correctamente (verifique mi Edición 1).
32
Luego establezca alguna acción ficticia en el Intent, de lo contrario, se eliminan los extras. Por ejemplo intent.setAction ("foo")
ognian
20
Excelente. Eso funciono. SetAction (Long.toString (System.currentTimeMillis ())) junto con el uso de FLAG_UPDATE_CURRENT que sugirió mbauer. Usar FLAG_ONE_SHOT solo me permitió hacer clic en la notificación una vez (lo cual tiene sentido). Muchas gracias ognian.
5
"Luego, establezca alguna acción ficticia en el Intent, de lo contrario, se eliminan los extras".
Mr_and_Mrs_D
El mecanismo setAction funcionó para mí. Por lo que está documentado, no estoy seguro, pero la fuente para Android está disponible en android.googlesource.com ;-)
Norman H
62

Estaba luchando con RemoteViewsy varios diferentes Intentspara cada uno Buttonen HomeScreenWidget. Funcionó cuando se agregaron estos:

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
fuente
+1 Genial Gracias. ¿Alguna idea de por qué agregar intent.setAction () lo hizo funcionar?
AjOnFire
setAction funciona, pero ¿qué pasa si realmente tengo que establecer mi acción de intenciones en otra cosa? ¿Por qué el marco tiene tantos errores?
b.lit
2
Me encanta cómo Android SDK es tan intuitivo para los desarrolladores ... (: ♥ ️ Por cierto, lea la respuesta de @ObjectiveTruth a continuación para obtener una explicación sobre el motivosetAction
Aviel Gross
1
Estaba obteniendo un comportamiento extraño, sin la llamada al método setAction, los extras de intención funcionarían durante la depuración, pero cuando no se depuraban, los extras de intención siempre serían los mismos que los extras iniciales pasados ​​en la primera llamada. Descubrí que mientras se depuraba, siempre se llamaba a onCreate cuando se navegaba fuera de la aplicación, pero mientras no se depuraba, no se llamaba a onCreate, solo a onStart. Llamar al método setAction resolvió el problema, supongo que tiene que ver con que las intenciones no sean 'diferentes' si solo ha cambiado el valor de los extras.
MaxJ
@clu Como ya estoy usando setAction, lo que puedes hacer es addCategory. PendingIntentse utiliza Intent.filterEqualspara comprobar la igualdad de la acción, los datos, el tipo, la clase y las categorías. developer.android.com/reference/android/content/…
iamreptar
43

Establecer acción Resolvió esto por mí. Aquí está mi comprensión de la situación:


Tengo varios widgets que tienen un PendingIntent adjunto a cada uno. Siempre que uno se actualizaba, todos se actualizaban. Las banderas están ahí para describir lo que sucede con PendingIntents que son exactamente iguales.

La descripción de FLAG_UPDATE_CURRENT se lee mucho mejor ahora:

Si ya existe el mismo PendingIntent que está creando, actualice todos los antiguos al nuevo PendingIntent que está creando.

La definición de exactamente lo mismo se aplica a todo el PendingIntent EXCEPTO los extras. Por lo tanto, incluso si tiene diferentes extras en cada intento (para mí, estaba agregando el appWidgetId) y luego en Android, son lo mismo.

Agregar .setAction con una cadena única ficticia le dice al sistema operativo. Estos son completamente diferentes y no actualizan nada. Al final, aquí está mi implementación que funciona como quería, donde cada widget tiene su propia configuración Intent adjunta:

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

ACTUALIZAR


Una solución aún mejor en caso de que esté trabajando con transmisiones. Los PendingIntents únicos también se definen mediante códigos de solicitud únicos. Esta es mi solución:

//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);
Verdad objetiva
fuente
1
Buena solución limpia
Varun Garg
1
Para mí, tener una identificación única y PendingIntent.FLAG_ONE_SHOT para la intención pendiente, junto con setAction en la intención funcionó.
Kaustuv
es un poco extraño cómo no está considerando cambios adicionales para el cambio de intención, pero parece ser cierto: /
zeroDivider
20

Veo respuestas pero no explicaciones. Además, ninguna de las respuestas aborda todas las posibles soluciones, así que intentaré aclararlo.

Documentación:

Si realmente necesita varios objetos PendingIntent distintos activos al mismo tiempo (por ejemplo, para usarlos como dos notificaciones que se muestran al mismo tiempo), entonces deberá asegurarse de que haya algo diferente en ellos para asociarlos con diferentes PendingIntents. Puede ser cualquiera de los atributos Intent considerados por Intent.filterEquals, o diferentes números enteros de código de solicitud proporcionados 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:

Creas 2 notificaciones con 2 intenciones pendientes. Cada intento pendiente está asociado con un intento:

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

Sin embargo, estos 2 intentos son iguales, por lo tanto, cuando llegue su segunda notificación, lanzará el primer intento.

Solución:

Tienes que hacer que cada intento sea único, para que ningún intento pendiente sea igual. ¿Cómo haces que las intenciones sean únicas? No por los extras que ponesputExtra() . Incluso si los extras son diferentes, las intenciones pueden ser iguales. Para que cada intención sea única, debe establecer un valor único para la acción, los datos, el tipo, la clase, la categoría o el código de solicitud de la intención: (cualquiera de ellos funcionará)

  • acción: intent.setAction(...)
  • datos: intent.setData(...)
  • tipo: intent.setType(...)
  • clase: intent.setClass(...)
  • categoría: intent.addCategory(...)
  • código de solicitud: PendingIntent.getActivity(context, YOUR_UNIQUE_CODE, intent, Intent.FLAG_ONE_SHOT);

Nota : Establecer un código de solicitud único puede ser complicado porque necesita un int, mientras que System.currentTimeMillis()devuelve long, lo que significa que se eliminarán algunos dígitos. Por lo tanto, recomendaría ir con la categoría o la acción y establecer una cadena única.

Steliosf
fuente
Esto es lo que finalmente funcionó para mí, usando una identificación única para cada notificación (necesaria de todos modos para la cancelabilidad) y una categoría personalizada por acción (nunca tendrá múltiples acciones del mismo tipo en la misma notificación).
MandisaW
Sí, también estoy usando una categoría única para cada intención, funciona muy bien.
steliosf
Tengo el mismo problema. dos notificaciones activadas al mismo tiempo. cuando hago clic en la segunda notificación, no pasa nada. después de establecer este setAction (Long.toString (System.currentTimeMillis ())); . está funcionando como un encanto. gracias por la bonita explicación @MScott
Anantha Babu
13

Tuve el mismo problema y pude solucionarlo cambiando la bandera a:

PendingIntent contentIntent = PendingIntent.getActivity(context, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
mbauer14
fuente
Muchas gracias por tomarse el tiempo para publicar lo que solucionó el problema. Olvidé mencionar que paso un extra al Intent. Esto hace que el problema sea un poco más complejo. Verifique mi edición 1.
9

Como la documentación dijo, use un código de solicitud único:

Si realmente necesita varios objetos PendingIntent distintos activos al mismo tiempo (por ejemplo, para usarlos como dos notificaciones que se muestran al mismo tiempo), entonces deberá asegurarse de que haya algo diferente en ellos para asociarlos con diferentes PendingIntents. Puede ser cualquiera de los atributos Intent considerados por Intent.filterEquals, o diferentes números enteros de código de solicitud proporcionados a getActivity (Context, int, Intent, int), getActivities (Context, int, Intent [], int), getBroadcast (Context, int , Intent, int) o getService (Context, int, Intent, int).

Tomasz
fuente
1
Esta es la única respuesta verdadera y precisa. Lo estaba buscando, porque quería publicar lo mismo. :-)
Sevastyan Savanyuk
7

Fwiw, he tenido más suerte con PendingIntent.FLAG_CANCEL_CURRENTque con PendingIntent.FLAG_UPDATE_CURRENT.

Jon Shemitz
fuente
Estoy totalmente de acuerdo con esto. No hay necesidad de llenar intents con extras inútiles si podemos hacer que cancele el anterior y luego cree uno nuevo. Es cierto que a veces puede resultar inútil si nada cambia, pero ahora la cuestión es "para ahorrar memoria o para ahorrar tiempo".
zeroDivider
4

Tuve el mismo problema y lo solucioné siguiendo los pasos a continuación

1) Borrar cualquier bandera de intención

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

2) inserte intent.setAction con el siguiente código

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

3) para Pendingintent, inserte el siguiente código

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

Espero trabajar contigo

Waleed A. Elgalil
fuente
1
Cualquiera se preocupa por explicar por qué se rechazó esta respuesta. Esto funcionó para mí. No sé si esta es una respuesta legítima, pero esta solución es la solución perfecta. Al menos para mi.
Sandeep R
¡Funcionó para mí también! ¡Gracias!
Andrés
2
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, Intent.FLAG_ACTIVITY_NEW_TASK);

En PendingIntent hay dos parámetros int, el segundo y el último. El segundo es el "código de solicitud" y debe ser un número único (por ejemplo, la identificación de su notificación), de lo contrario, si (como en su ejemplo, es igual a cero, siempre se sobrescribirá).

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

para enviar datos extra correctamente, debe enviar con intención pendiente la identificación de notificación como esta: PendingIntent pendienteIntent = PendingIntent.getActivity (context, (int) System.currentTimeMillis () , intent, PendingIntent.FLAG_UPDATE_CURRENT);

Amal Kronz
fuente
0

Tengo el mismo problema y uso PendingIntent.html.FLAG_UPDATE_CURRENT para solucionarlo.

He comprobado el código fuente. En ActivityManagerService.java , el método clave es el siguiente. Cuando la bandera es PendingIntent.FLAG_UPDATE_CURRENT y updateCurrent es verdadero. Algunos extras serán reemplazados por nuevos y obtendremos un PendingIntent reemplazado.

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

Tuve el mismo problema y pude solucionarlo cambiando la bandera a:

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
fuente
4
No tiene nada que ver con una pregunta formulada.
Paul Turchenko
Necesito aclarar cómo se relaciona con esta pregunta.
Norman H