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

Это имеет большое значение, потому что мне нужен дополнительный «ответ», чтобы отразить, каким был ответ String при создании уведомления. Вместо этого при использовании PendingIntent.FLAG_UPDATE_CURRENTдополнительный «ответ» отражает ответ String на последний вызов displayNotification().

Я знаю, почему это происходит из документации FLAG_UPDATE_CURRENT. Однако я не знаю, как это обойти в данный момент.

Капил Раджпут
источник

Ответы:

125

Не используйте Intent.FLAG_ACTIVITY_NEW_TASKдля PendingIntent.getActivity, используйте вместо этого FLAG_ONE_SHOT


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

Затем установите какое-нибудь фиктивное действие для Intent, иначе дополнительные функции будут отброшены. Например

intent.setAction(Long.toString(System.currentTimeMillis()))
Огнян
источник
Этот флаг на самом деле не работал по той же причине, я думаю, что мой дополнительный не работает правильно (проверьте мой Edit 1).
32
Затем установите какое-нибудь фиктивное действие для Intent, иначе дополнительные функции будут отброшены. Например intent.setAction ("foo")
ognian 06
20
Превосходно. Это сработало. Я установил действие (Long.toString (System.currentTimeMillis ())) в сочетании с использованием FLAG_UPDATE_CURRENT, которое предложил mbauer. Использование FLAG_ONE_SHOT позволило мне щелкнуть уведомление только один раз (что имеет смысл). Большое спасибо, Огниан.
5
«Затем установите какое-нибудь фиктивное действие для Intent, иначе дополнительные функции будут сброшены» - это где-то задокументировано?
Mr_and_Mrs_D
У меня сработал механизм setAction. Насколько это задокументировано, не уверен, но исходный код для Android доступен на android.googlesource.com ;-)
Norman H
62

Борется с RemoteViewsи несколькими различными Intentsдля каждого Buttonна HomeScreenWidget. Сработало при добавлении этих:

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 08
setAction работает, но что, если мне действительно нужно установить действие намерений на что-то другое? Почему фреймворк такой глючный?
b.lit 08
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

Установить действие Решил это для меня. Вот мое понимание ситуации:


У меня есть несколько виджетов, к каждому из которых прикреплен 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);

ОБНОВИТЬ


Еще лучшее решение, если вы работаете с трансляциями. Уникальные PendingIntents также определяются уникальными кодами запроса. Вот мое решение:

//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
источник
1
Хорошее чистое решение
Варун Гарг
1
У меня есть уникальный идентификатор и PendingIntent.FLAG_ONE_SHOT для ожидающего намерения вместе с setAction для намерения.
Kaustuv
немного странно, что он не рассматривает дополнительные изменения для изменения намерения, но кажется правдой: /
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()возвращает long, что означает, что некоторые цифры будут удалены. Поэтому я бы рекомендовал выбрать категорию или действие и установить уникальную строку.

стелиос
источник
Это то, что в конечном итоге сработало для меня, используя уникальный идентификатор для каждого уведомления (в любом случае необходимо для отмены) и настраиваемую категорию для каждого действия (никогда не будет нескольких действий одного типа в одном уведомлении).
MandisaW 07
Ага, я тоже использую уникальную категорию для каждого намерения, это отлично работает.
steliosf
Есть такая же проблема. два уведомления срабатывают одновременно. когда я нажимаю второе уведомление, ничего не произошло. после установки этого setAction (Long.toString (System.currentTimeMillis ())); . его работа как шарм. спасибо за хорошее объяснение @MScott
Babu
13

У меня была такая же проблема, и я смог ее исправить, изменив флаг на:

PendingIntent contentIntent = PendingIntent.getActivity(context, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
mbauer14
источник
Большое спасибо за то, что нашли время, чтобы опубликовать, что решило проблему для вас. Я забыл упомянуть, что передаю в Intent дополнительную информацию. Это немного усложняет проблему. Check my Edit 1.
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 вставьте приведенный ниже код

   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, второй и последний. Второй - это «код запроса», и это должен быть уникальный номер (например, идентификатор вашего уведомления), иначе, если (как в вашем примере он равен нулю, он всегда будет перезаписан).

Дмитрий Убогий
источник
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();

    }
user1917789
источник
4
Это не имеет ничего общего с заданным вопросом.
Павел Турченко
Нужно уточнить, как это относится к этому вопросу.
Norman H