PendingIntent działa poprawnie dla pierwszego powiadomienia, ale nieprawidłowo dla pozostałych

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

Ta funkcja będzie wywoływana wiele razy. Chciałbym, aby każdy notificationuruchamiał testActivity po kliknięciu. Niestety, dopiero pierwsze powiadomienie uruchamia testActivity. Kliknięcie na resztę powoduje zminimalizowanie okna powiadomienia.

Dodatkowe informacje: Funkcja displayNotification()znajduje się w klasie o nazwie UploadManager. Contextjest przekazywany UploadManagerz tego, activityco tworzy instancję. Funkcja displayNotification()jest wywoływana wiele razy z funkcji, również w programie UploadManager, która działa w pliku AsyncTask.

Edycja 1: zapomniałem wspomnieć, że przekazuję odpowiedź typu String do Intent intentpliku 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);

To robi dużą różnicę, ponieważ potrzebuję dodatkowej „odpowiedzi”, aby odzwierciedlić, jaka była odpowiedź typu String w momencie tworzenia powiadomienia. Zamiast tego, PendingIntent.FLAG_UPDATE_CURRENTdodatkowa „odpowiedź” odzwierciedla odpowiedź typu String podczas ostatniego wywołania displayNotification().

Wiem, dlaczego to jest po przeczytaniu dokumentacji FLAG_UPDATE_CURRENT. Jednak w tej chwili nie wiem, jak to obejść.

Kapil Rajput
źródło

Odpowiedzi:

125

Nie używaj Intent.FLAG_ACTIVITY_NEW_TASKdo PendingIntent.getActivity, zamiast tego użyj FLAG_ONE_SHOT


Skopiowano z komentarzy:

Następnie ustaw fikcyjną akcję na intencji, w przeciwnym razie dodatki zostaną odrzucone. Na przykład

intent.setAction(Long.toString(System.currentTimeMillis()))
ognian
źródło
Ta flaga faktycznie również nie działała z tego samego powodu, myślę, że mój dodatek nie działa poprawnie (sprawdź moją Edycję 1).
32
Następnie ustaw fikcyjną akcję na intencji, w przeciwnym razie dodatki zostaną odrzucone. Na przykład intent.setAction ("foo")
ognian
20
Świetny. To się udało. I setAction (Long.toString (System.currentTimeMillis ())) w połączeniu z użyciem FLAG_UPDATE_CURRENT zasugerował mbauer. Użycie FLAG_ONE_SHOT pozwoliło mi kliknąć powiadomienie tylko raz (co ma sens). Wielkie dzięki ognian.
5
„Następnie ustaw jakąś fikcyjną akcję na intencji, w przeciwnym razie dodatki zostaną usunięte” - czy jest to gdzieś udokumentowane?
Mr_and_Mrs_D
U mnie zadziałał mechanizm setAction. O ile to udokumentowane, nie jestem pewien, ale źródło dla Androida jest dostępne pod adresem android.googlesource.com ;-)
Norman H
62

Walczył z RemoteViewskilkoma różnymi Intentsdla każdego Buttonna HomeScreenWidget. Działał po dodaniu tych:

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
źródło
+1 fajne dzięki. Masz jakiś pomysł, dlaczego dodanie funkcji intent.setAction () sprawiło, że to zadziałało?
AjOnFire
setAction działa, ale co się stanie, jeśli faktycznie muszę ustawić swoje zamiary Action na coś innego? Dlaczego framework jest tak błędny?
Świeci
2
Po prostu uwielbiam to, że Android SDK jest tak intuicyjny dla programistów ... (: ♥ ️ BTW przeczytaj odpowiedź @ObjectiveTruth poniżej, aby wyjaśnić powódsetAction
Aviel Gross
1
Otrzymywało dziwne zachowanie, bez wywołania metody setAction dodatki intencji działałyby podczas debugowania, ale gdy nie były debugowane, dodatki intencji zawsze byłyby takie same jak początkowe dodatki przekazane w pierwszym wywołaniu. Zauważyłem, że podczas debugowania onCreate był zawsze wywoływany podczas wychodzenia z aplikacji, ale bez debugowania onCreate nie był wywoływany, tylko onStart. Wywołanie metody setAction rozwiązało problem, domyślam się, że jest to spowodowane tym, że intencje nie są „różne”, jeśli zmieniła się tylko wartość dodatków.
MaxJ
@clu Ponieważ już używam setAction, możesz zrobić addCategory. PendingIntentużywa Intent.filterEqualsdo sprawdzania równości akcji, danych, typu, klasy i kategorii. developer.android.com/reference/android/content/…
iamreptar
43

Ustaw działanie rozwiązało to dla mnie. Oto moje rozumienie sytuacji:


Mam wiele widżetów, do których dołączony jest PendingIntent. Za każdym razem, gdy ktoś został zaktualizowany, wszystkie zostały zaktualizowane. Flagi są tam, aby opisać, co dzieje się z PendingIntents, które są dokładnie takie same.

Opis FLAG_UPDATE_CURRENT czyta się teraz znacznie lepiej:

Jeśli ten sam PendingIntent, który tworzysz, już istnieje, zaktualizuj wszystkie stare do nowego PendingIntent, który tworzysz.

Definicja dokładnie tego samego wygląda na cały PendingIntent Z WYJĄTKIEM dodatków. Tak więc nawet jeśli masz różne dodatki w każdej intencji (dla mnie dodawałem appWidgetId), to do Androida są takie same.

Dodanie .setAction z fałszywym unikalnym ciągiem informuje system operacyjny. Są zupełnie inne i niczego nie aktualizują. Na koniec oto moja implementacja, która działa tak, jak chciałem, gdzie każdy widżet ma dołączoną własną intencję konfiguracji:

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

AKTUALIZACJA


Jeszcze lepsze rozwiązanie w przypadku pracy z transmisjami. Unikalne PendingIntents są również definiowane przez unikalne kody żądań. Oto moje rozwiązanie:

//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
źródło
1
Niezłe, czyste rozwiązanie
Varun Garg
1
Dla mnie posiadanie unikalnego identyfikatora i PendingIntent.FLAG_ONE_SHOT dla oczekujących intencji, wraz z setAction na intencji zadziałało.
Kaustuv
trochę dziwne, że nie bierze pod uwagę zmian dodatków do zamiaru zmiany, ale wydaje się, że to prawda: /
zeroDivider
20

Widzę odpowiedzi, ale żadnych wyjaśnień. Żadna z odpowiedzi również nie dotyczy wszystkich możliwych rozwiązań, więc postaram się to wyjaśnić.

Dokumentacja:

Jeśli naprawdę potrzebujesz wielu różnych obiektów PendingIntent aktywnych w tym samym czasie (na przykład jako dwóch powiadomień, które są wyświetlane w tym samym czasie), musisz upewnić się, że jest w nich coś innego, aby powiązać je z różnymi PendingIntents. Może to być dowolny z atrybutów Intent rozpatrywanych przez Intent.filterEquals lub różne liczby całkowite kodu żądania dostarczone do getActivity (Context, int, Intent, int), getActivities (Context, int, Intent [], int), getBroadcast (Context, int) , Intent, int) lub getService (Context, int, Intent, int).

Przyczyna problemu:

Tworzysz 2 powiadomienia z 2 oczekującymi intencjami. Każda oczekująca intencja jest powiązana z intencją:

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

Jednak te 2 intencje są równe, dlatego gdy nadejdzie drugie powiadomienie, uruchomi ono pierwszą intencję.

Rozwiązanie:

Musisz uczynić każdą intencję wyjątkową, aby żadne oczekujące intencje nigdy nie były równe. Jak sprawić, by intencje były wyjątkowe? Nie przez dodatki, które włożyłeś putExtra(). Nawet jeśli dodatki są różne, intencje mogą być jednakowe. Aby każda intencja była unikalna, musisz ustawić unikatową wartość akcji zamierzonej, danych, typu, klasy lub kategorii lub kodu żądania: (każdy z nich zadziała)

  • akcja: intent.setAction(...)
  • dane: intent.setData(...)
  • rodzaj: intent.setType(...)
  • klasa: intent.setClass(...)
  • Kategoria: intent.addCategory(...)
  • Kod zapytania: PendingIntent.getActivity(context, YOUR_UNIQUE_CODE, intent, Intent.FLAG_ONE_SHOT);

Uwaga : ustawienie unikalnego kodu żądania może być trudne, ponieważ potrzebujesz int, a System.currentTimeMillis()zwraca long, co oznacza, że ​​niektóre cyfry zostaną usunięte. Dlatego radziłbym wybrać kategorię lub akcję i ustawić unikalny ciąg.

steliosf
źródło
To właśnie ostatecznie zadziałało, używając unikalnego identyfikatora dla każdego powiadomienia (i tak potrzebnego do anulowania) i niestandardowej kategorii na akcję (nigdy nie będzie mieć wielu działań tego samego typu dla tego samego powiadomienia).
MandisaW
Tak, używam osobnej kategorii dla każdego celu, to działa świetnie.
steliosf
Mam ten sam problem. dwa powiadomienia uruchomione w tym samym czasie. kiedy kliknę drugie powiadomienie, nic się nie stało. po ustawieniu tego setAction (Long.toString (System.currentTimeMillis ())); . działa jak urok. dzięki za miłe wyjaśnienie @MScott
Anantha Babu
13

Miałem ten sam problem i udało mi się go naprawić, zmieniając flagę na:

PendingIntent contentIntent = PendingIntent.getActivity(context, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
mbauer14
źródło
Wielkie dzięki za poświęcenie czasu na opublikowanie tego, co rozwiązało problem. Zapomniałem wspomnieć, że przekazuję dodatkowy element Intencji. To sprawia, że ​​problem jest nieco bardziej złożony. Sprawdź moją
9

Zgodnie z dokumentacją użyj unikalnego kodu żądania:

Jeśli naprawdę potrzebujesz wielu różnych obiektów PendingIntent aktywnych w tym samym czasie (na przykład jako dwóch powiadomień, które są wyświetlane w tym samym czasie), musisz upewnić się, że jest w nich coś innego, aby powiązać je z różnymi PendingIntents. Może to być dowolny z atrybutów Intent rozpatrywanych przez Intent.filterEquals lub różne liczby całkowite kodu żądania dostarczone do getActivity (Context, int, Intent, int), getActivities (Context, int, Intent [], int), getBroadcast (Context, int) , Intent, int) lub getService (Context, int, Intent, int).

Tomasz
źródło
1
To jedyna prawdziwa i właściwa odpowiedź. Szukałem tego, bo chciałem opublikować to samo. :-)
Sevastyan Savanyuk
7

Fwiw, miałem więcej szczęścia PendingIntent.FLAG_CANCEL_CURRENTniż z PendingIntent.FLAG_UPDATE_CURRENT.

Jon Shemitz
źródło
Całkowicie się z tym zgadzam. Nie ma potrzeby wypełniania intencji bezużytecznymi dodatkami, jeśli uda nam się anulować stary, a następnie utworzyć nowy. To prawda, że ​​czasami może się to okazać bezużyteczne, jeśli nic się nie zmieni, ale teraz pytanie brzmi: „oszczędzać pamięć lub oszczędzać czas”.
zeroDivider
4

Miałem ten sam problem i naprawiłem go, wykonując poniższe czynności

1) Wyczyść wszelkie flagi celowe

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

2) wstaw intent.setAction za pomocą poniższego kodu

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

3) dla Pendingintent wprowadź poniższy kod

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

Mam nadzieję, że będę z tobą pracować

Waleed A. Elgalil
źródło
1
Każdy chciałby wyjaśnić, dlaczego ta odpowiedź została odrzucona. To zadziałało dla mnie. Nie wiem, czy to jest słuszna odpowiedź, ale to rozwiązanie jest idealne. Przynajmniej dla mnie.
Sandeep R
U mnie też zadziałało! Dzięki!
Andres
2
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, Intent.FLAG_ACTIVITY_NEW_TASK);

W PendingIntent są dwa parametry int, drugi i ostatni. Drugi to „kod żądania” i musi to być niepowtarzalny numer (np. Identyfikator Twojego zgłoszenia), w przeciwnym razie (tak jak w Twoim przykładzie jest równy zero, zawsze zostanie nadpisany).

Dmytro Ubogyi
źródło
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
źródło
0

aby wysłać dane wyjątkowo poprawnie, należy wysłać z intencją oczekującą identyfikator powiadomienia w następujący sposób: PendingIntent pendingIntent = PendingIntent.getActivity (context, (int) System.currentTimeMillis () , intent, PendingIntent.FLAG_UPDATE_CURRENT);

Amal Kronz
źródło
0

Mam ten sam problem i używam PendingIntent.html.FLAG_UPDATE_CURRENT, aby go naprawić.

Sprawdziłem kod źródłowy. W ActivityManagerService.java metoda klucza jest następująca. Gdy flaga ma wartość PendingIntent.FLAG_UPDATE_CURRENT, a parametr updateCurrent ma wartość true. Niektóre dodatki zostaną zastąpione nowymi, a my otrzymamy zmieniony 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);
        }

qin hao
źródło
-5

Miałem ten sam problem i udało mi się go naprawić, zmieniając flagę na:

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
źródło
4
Nie ma to nic wspólnego z zadanym pytaniem.
Paul Turchenko,
Trzeba wyjaśnić, jak to się ma do tego pytania.
Norman H