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

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

Большое спасибо за то, что нашли время, чтобы опубликовать, что решило проблему для вас. Я забыл упомянуть, что передаю в 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();

    }

4
Это не имеет ничего общего с заданным вопросом.
Павел Турченко

Нужно уточнить, как это относится к этому вопросу.
Norman H
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.