PendingIntent працює правильно для першого сповіщення, але неправильно для решти


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

Цю функцію буде викликано кілька разів. Я хотів би, щоб кожен notificationзапустив testActivity при натисканні. На жаль, лише перше сповіщення запускає testActivity. Натискання решти змушує вікно сповіщень звести до мінімуму.

Додаткова інформація: Функція displayNotification()знаходиться у класі, який називається UploadManager. Contextпередається в UploadManagerз activityекземпляра, який створює. Функція displayNotification()викликається кілька разів із функції, також у UploadManager, яка працює в AsyncTask.

Редагування 1: Я забув згадати, що передаю відповідь String Intent intentяк файл 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);

Це має велике значення, оскільки мені потрібна додаткова "відповідь", щоб відобразити, якою була відповідь рядка, коли було створено повідомлення. Натомість, використовуючи PendingIntent.FLAG_UPDATE_CURRENT, додатковий "відповідь" відображає те, якою була відповідь рядка на останній дзвінок displayNotification().

Я знаю, чому це з прочитання документації на FLAG_UPDATE_CURRENT. Однак я не знаю, як це обійти на даний момент.

Відповіді:


125

Не використовуйте Intent.FLAG_ACTIVITY_NEW_TASKдля PendingIntent.getActivity, натомість використовуйте FLAG_ONE_SHOT


Скопійовано з коментарів:

Потім встановіть фіктивну дію на намір, інакше додаткові послуги скидаються. Наприклад

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

Цей прапор насправді також не працював з тієї самої причини, я думаю, що моя додаткова програма працює неправильно (перевірте мій Редагувати 1).

32
Потім встановіть фіктивну дію на намір, інакше додаткові послуги скидаються. Наприклад intent.setAction ("foo")
ognian

20
Відмінно. Це спрацювало. Я встановив ActionAction (Long.toString (System.currentTimeMillis ())) у поєднанні з використанням FLAG_UPDATE_CURRENT, яке запропонував mbauer. Використання FLAG_ONE_SHOT дозволило мені натиснути сповіщення лише один раз (що має сенс). Велике спасибі ognian.

5
"Тоді встановіть якусь фіктивну дію щодо Наміру, інакше додаткові послуги скидаються" - це десь задокументовано?
Mr_and_Mrs_D

Механізм setAction працював у мене. Наскільки це задокументовано, не впевнений, але джерело для Android доступне за адресою android.googlesource.com ;-)
Норман Х

62

Боровся з RemoteViewsкількома різними Intentsдля кожного Buttonна HomeScreenвіджеті. Працювали, коли додавали:

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 Класно Спасибі. Будь-яка ідея, чому додавання intent.setAction () змусило це працювати?
AjOnFire

setAction працює, але що, якщо я дійсно повинен встановити свої наміри Action на щось інше? Чому фреймворк такий глючний?
b.lit

2
Мені просто подобається, як Android SDK настільки інтуїтивно зрозумілий для розробників ... (: ♥ ️ До речі, прочитайте відповідь @ObjectiveTruth нижче для пояснення причиниsetAction
Aviel Gross

1
Отримувала дивну поведінку, без використання методу setAction додаткові наміри викликів працювали б під час налагодження, але коли не налагоджувались, додаткові наміри завжди були такими ж, як початкові додаткові дані, передані під час першого виклику. Я виявив, що під час налагодження, onCreate завжди викликався при виході з програми, але хоча і не налагоджував, onCreate не викликався, лише onStart. Виклик методу setAction вирішив проблему, я здогадуюсь, це щось спільне з намірами, які не є "різними", якщо змінилося лише значення додатків.
MaxJ

@clu Оскільки я вже використовую setAction, що ти можеш зробити, це addCategory. PendingIntentвикористовує Intent.filterEqualsдля перевірки рівності дії, даних, типу, класу та категорій. developer.android.com/reference/android/content/…
iamreptar

43

Set Action Вирішив це для мене. Ось моє розуміння ситуації:


У мене є кілька віджетів, до яких прикріплений PendingIntent. Щоразу, коли хтось оновлювався, вони всі оновлювались. Прапори є, щоб описати, що відбувається з PendingIntents, які абсолютно однакові.

FLAG_UPDATE_CURRENT опис читається набагато краще зараз:

Якщо той самий PendingIntent, який ви робите, вже існує, оновіть усі старі до нового PendingIntent, який ви робите.

Визначення точно такого ж розглядає весь PendingIntent, окрім додаткових. Таким чином, навіть якщо у вас є різні додаткові функції щодо кожного наміру (для мене я додавав appWidgetId), то до android, вони однакові.

Додавання .setAction з деяким фіктивним унікальним рядком повідомляє ОС. Вони абсолютно різні і нічого не оновлюють. Врешті-решт, ось моя реалізація, яка працює так, як я хотів, де кожен віджет має свій власний конфігураційний намір:

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

ОНОВЛЕННЯ


Ще краще рішення, якщо ви працюєте з трансляціями. Унікальні очікувані інтенції також визначаються унікальними кодами запитів. Ось моє рішення:

//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
Nice Clean Solution
Варун Гарг

1
Для мене працював унікальний ідентифікатор та PendingIntent.FLAG_ONE_SHOT для очікуваного наміру разом із setAction on intent.
Каустув

трохи дивно, як це не враховує додаткові зміни для зміни наміру, але, здається, це правда: /
zeroDivider

20

Я бачу відповіді, але ніяких пояснень. Також жодна з відповідей не стосується усіх можливих рішень, тому я спробую це пояснити.

Документація:

Якщо вам дійсно потрібні кілька різних об'єктів PendingIntent, які активні одночасно (наприклад, використовувати як два сповіщення, які одночасно відображаються одночасно), то вам потрібно буде переконатися, що в них є щось інше, щоб асоціювати їх з різними PendingIntents. Це може бути будь-який з атрибутів Intent, що розглядаються Intent.filterEquals, або різні цілі числа коду запиту, що надаються для getActivity (Context, int, Intent, int), getActivities (Context, int, Intent [], int), getBroadcast (Context, int , Intent, int), або getService (Context, int, Intent, int).

Причина проблеми:

Ви створюєте 2 сповіщення з 2 очікуваними намірами. Кожен намір, що очікує, асоціюється з наміром:

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

Однак ці 2 наміри рівні, тому, коли надійде ваше друге сповіщення, воно розпочне перший намір.

Рішення:

Ви повинні зробити кожен намір унікальним, щоб жоден намір, що очікує, ніколи не був рівним. Як зробити наміри унікальними? Не за допомогою додаткових послуг, з якими ви ставите putExtra(). Навіть якщо додаткові послуги різні, наміри все одно можуть бути однаковими. Щоб зробити кожен намір унікальним, ви повинні встановити унікальне значення для дії наміру, або даних, або типу, або класу, або категорії, або коду запиту: (будь-який із них буде працювати)

  • дія: intent.setAction(...)
  • дані: intent.setData(...)
  • тип: intent.setType(...)
  • клас: intent.setClass(...)
  • категорія: intent.addCategory(...)
  • код запиту: PendingIntent.getActivity(context, YOUR_UNIQUE_CODE, intent, Intent.FLAG_ONE_SHOT);

Примітка : Встановлення унікального коду запиту може бути складним, оскільки вам потрібен int, а System.currentTimeMillis()повертається довго, що означає, що деякі цифри будуть видалені. Тому я рекомендую або вибрати категорію, або дію та встановити унікальний рядок.


Це те, що врешті-решт спрацювало для мене, використовуючи унікальний ідентифікатор для кожного повідомлення (в будь-якому випадку необхідний для скасування) та спеціальну категорію на дію (ніколи не матиме декількох дій одного типу в одному повідомленні).
MandisaW

Так, я також використовую унікальну категорію для кожного наміру, це чудово працює.
steliosf

У мене така сама проблема. два сповіщення, що спрацьовують одночасно. коли я натискаю друге сповіщення, нічого не сталося. після встановлення цього setAction (Long.toString (System.currentTimeMillis ())); . це працює як шарм. спасибі за приємне пояснення @MScott
Babu

13

У мене була та ж проблема, і я зміг її виправити, змінивши прапор на:

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

Велике спасибі, що знайшли час, щоб опублікувати те, що вирішило проблему. Я забув згадати, що передаю зайвий у Намір. Це ускладнює проблему. Перевірте мою

9

Як зазначено в документації, використовуйте унікальний код запиту:

Якщо вам дійсно потрібні кілька різних об'єктів PendingIntent, які активні одночасно (наприклад, використовувати як два сповіщення, які одночасно відображаються одночасно), то вам потрібно буде переконатися, що в них є щось інше, щоб асоціювати їх з різними PendingIntents. Це може бути будь-який з атрибутів Intent, що розглядаються Intent.filterEquals, або різні цілі числа коду запиту, що надаються для getActivity (Context, int, Intent, int), getActivities (Context, int, Intent [], int), getBroadcast (Context, int , Intent, int), або getService (Context, int, Intent, int).


1
Це єдина вірна і права на точну відповідь. Шукав, бо я хотів опублікувати те саме. :-)
Севастьян Саванюк

7

Fwiw, мені пощастило PendingIntent.FLAG_CANCEL_CURRENTбільше, ніж з PendingIntent.FLAG_UPDATE_CURRENT.


Я повністю з цим згоден. Немає необхідності заповнювати наміри марними додатками, якщо ми можемо змусити скасувати старий, а потім створити новий. Це правда, що іноді це може виявитися марним, якщо нічого не змінилося, але зараз питання полягає в "економії пам'яті або економії часу".
zeroDivider

4

У мене була та сама проблема, і я виправив її за допомогою наступних кроків

1) Очистіть будь-який прапор для наміру

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

2) вставте intent.setAction за наведеним нижче кодом

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

3) для очікуваного наміру вставте код нижче

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

Я сподіваюся працювати з вами


1
Будь-хто хоче пояснити, чому цю відповідь було проти. Це спрацювало для мене. Я не знаю, чи є це законною відповіддю, але це рішення є ідеальним рішенням. Щонайменше для мене.
Sandeep R,

Працював і у мене! Дякую!
Андрес,

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

У PendingIntent є два параметри int, другий і останній. Другий - це "код запиту", і це повинен бути номер unicue (наприклад, ідентифікатор вашого повідомлення), інакше якщо (як у вашому прикладі він дорівнює нулю, він завжди буде перезаписаний).


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

щоб надмірно правильно надіслати дані, слід надіслати з очікуваним наміром ідентифікатор сповіщення таким чином: PendingIntent pendingIntent = PendingIntent.getActivity (context, (int) System.currentTimeMillis () , intent, PendingIntent.FLAG_UPDATE_CURRENT);


0

У мене така сама проблема, і використовуйте PendingIntent.html.FLAG_UPDATE_CURRENT, щоб її виправити.

Я перевірив вихідний код. У ActivityManagerService.java ключовим методом є такий. Коли прапор PendingIntent.FLAG_UPDATE_CURRENT, а updateCurrent - істина. Деякі додаткові послуги будуть замінені новими, і ми отримаємо замінений 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);
        }


-5

У мене була та ж проблема, і я зміг її виправити, змінивши прапор на:

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
Це не має нічого спільного із заданим питанням.
Павло Турченко

Потрібно пояснити, як це стосується цього питання.
Norman H
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.