Zamiar - jeśli aktywność jest uruchomiona, przenieś ją na wierzch, w przeciwnym razie rozpocznij nową (z powiadomienia)

131

Moja aplikacja ma powiadomienia, które - oczywiście - bez żadnych flag, za każdym razem rozpoczynają nowe działanie, więc otrzymuję wiele takich samych działań działających jedna na drugiej, co jest po prostu błędne.

To, co chcę, to przenieść aktywność określoną w oczekujących powiadomieniach na pierwszy plan, jeśli jest już uruchomiona, w przeciwnym razie ją uruchomić.

Jak dotąd intencja / oczekująca intencja dla tego powiadomienia, które mam, to

private static PendingIntent prepareIntent(Context context) {
    Intent intent = new Intent(context, MainActivity.class);
    intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);

    return PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}

i co dziwne, czasami działa, czasami nie ... Wydaje mi się, że wypróbowałem już każdą kombinację flag.

urSus
źródło

Odpowiedzi:

134

Możesz użyć tego:

<activity 
   android:name=".YourActivity"
   android:launchMode="singleTask"/>

który będzie działał podobnie, "singleInstance"ale nie będzie miał tej dziwnej animacji.

Franco
źródło
15
+1, powinieneś być zaakceptowaną odpowiedzią. Różnice są tutaj: developer.android.com/guide/topics/manifest/ ...
user1032613
1
Aby wyjaśnić, użyj funkcji singleTask, jeśli masz wiele działań w aplikacji i jest to nadrzędna, pojedyncza instancja, jeśli masz tylko jedno działanie.
mroźno cudowny
5
Jeśli ktoś taki jak ja wypróbował wiele kombinacji rozwiązań, zanim znalazły się na tej stronie: Upewnij się, że pozbyłeś się innych zmian, które wypróbowałeś, i upewnij się, że wprowadziłeś tę zmianę do prawidłowego działania. Zbyt długo zajęło mi odkrycie, że to rozwiązanie zadziałałoby, gdybym nie wypróbował również innych rozwiązań (np. Nadpisywanie onNewIntenti ustawianie flag)
lucidbrot
Mam mapę Messenger, para Aktywność. Chcę wyświetlić instancję Activity, jeśli otrzymam od niej wiadomość. Czy jest na to sposób. np .: HashMap.get (Messenger) .bringActivityToFront (); To znaczy, aktywność wciąż trwa. Jeśli otworzę ostatnie zadanie i kliknę je, włączy się onResume (); Nie mogę programowo onResume ().
@JaveneCPPMcGowan, które wydaje się zupełnie innym pytaniem, sugeruję, abyś opublikował to jako własne pytanie, niestety tak naprawdę nie znam odpowiedzi.
Franco
47

Myślę, że najlepszym sposobem na zrobienie tego i w prosty sposób jest normalne rozpoczęcie działania, ale ustawienie tego działania w manifeście z singleInstancewłaściwością. Dzięki temu praktycznie podchodzisz do obu problemów, które masz w tej chwili, przenosząc aktywność na pierwszy plan przez cały czas i pozwalając systemowi operacyjnemu automatycznie tworzyć nowy, jeśli żadna aktywność nie istnieje, lub przenosić na wierzch już istniejącą aktywność (dzięki singleInstancewłasność).

Oto sposób deklarowania działania jako pojedynczej instancji:

<activity 
   android:name=".YourActivity"
   android:launchMode="singleInstance"/>

Aby uniknąć przerywanej animacji podczas uruchamiania przez singleInstance, możesz użyć zamiast tego „singleTask”, oba są bardzo podobne, ale różnica jest wyjaśniona tutaj zgodnie z dokumentacją Google:

<activity 
   android:name=".YourActivity"
   android:launchMode="singleTask"/>

singleInstance to to samo, co „singleTask”, z tą różnicą, że system nie uruchamia żadnych innych działań w zadaniu, w którym znajduje się instancja. Działalność jest zawsze jedynym i jedynym członkiem jej zadania.

Mam nadzieję że to pomoże.

Pozdrowienia!

Martin Cazares
źródło
4
wydaje się, że działa, ale teraz każda inna aktywność, którą uruchamiam z singlaIntent MainActivity, odtwarzam tę dziwną animację łuku zamiast zwykłego zanikania i skalowania
urSus
A co z tym, że za każdym razem uruchamiałem nowe działanie, ale wyczyściłem wszystkie inne uruchomione wystąpienia, czy to byłoby możliwe?
urSus
Cóż, nie sądzę, żeby animacja, o której wspominasz, miała cokolwiek wspólnego z właściwością "singleInstance", wszystko co robi to ponowne wykorzystanie istniejącej instancji, może ta animacja, o której mówisz, to zupełnie inny problem ...
Martin Cazares
1
cóż, testuję to teraz i tak jest. Jeśli usunę wszystkie flagi intencji i po prostu ustawię launchMode = "singleInstance" w manifeście, pojawi się dziwna animacja, jeśli ją
usunę
Tak, zgodnie z dokumentacją Androida wygląda na to, że animacja "init" nie jest już dostępna jak zwykle, ponieważ aktywność nie jest inicjowana, jest tylko ponownie używana ...
Martin Cazares
27

Myślę, że to, czego potrzebujesz, jest w singleTop Activity, a nie singleTasklub singleInstance.

<activity android:name=".MyActivity"
          android:launchMode="singleTop"
          ...the rest... >

To, co mówi dokumentacja, doskonale odpowiada Twoim potrzebom:

[...] singleTopmożna również utworzyć nową instancję działania „ ” w celu obsługi nowej intencji. Jeśli jednak zadanie docelowe ma już istniejącą instancję działania na szczycie swojego stosu, instancja ta otrzyma nową intencję (w onNewIntent()wywołaniu); nowa instancja nie jest tworzona. W innych okolicznościach - na przykład, jeśli istniejąca instancja działania „ singleTop” znajduje się w zadaniu docelowym, ale nie znajduje się na szczycie stosu, lub jeśli znajduje się na szczycie stosu, ale nie w zadaniu docelowym - nowy wystąpienie zostanie utworzone i umieszczone na stosie.

Poza tym (gra słów nie była zamierzona), miałem dokładnie takie same potrzeby jak ty. Przetestowałem wszystkie launchModeflagi, aby dowiedzieć się, jak faktycznie zachowują się w praktyce i w rezultacie singleTopjest do tego najlepsza: bez dziwnej animacji, aplikacja wyświetlana raz na liście ostatnich aplikacji (w przeciwieństwie do singleInstancetego, że wyświetla ją dwukrotnie, ponieważ nie działa) nie pozwalać komukolwiek innemu Activityna bycie częścią jego zadania) i właściwego zachowania, niezależnie od tego, czy cel Activityjuż istnieje, czy nie.

Shlublu
źródło
3
Pracuję do 22:00 w piątek wieczorem i właśnie tego chcę ... Smutne życie dewelopera.
felixwcf
Zaoszczędziłem dużo czasu! Dziękuję Ci!
hetsgandhi
13

To stary wątek, ale dla wszystkich, którzy wciąż szukają odpowiedzi, oto moje rozwiązanie. Jest wyłącznie w kodzie, bez ustawień manifestu:

private static PendingIntent prepareIntent(Context context) {
  Intent intent = new Intent(context, MainActivity.class);
  intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);      
  return PendingIntent.getActivity(context, NON_ZERO_REQUEST_CODE, intent, 
    PendingIntent.FLAG_UPDATE_CURRENT);
}

Tutaj FLAG_ACTIVITY_SINGLE_TOP otwiera istniejącą aktywność, jeśli znajduje się na szczycie stosu zadań. Jeśli nie jest na górze, ale wewnątrz stosu, FLAG_ACTIVITY_CLEAR_TOP usunie wszystkie działania znajdujące się na górze działania docelowego i pokaże je. Jeśli aktywność nie znajduje się na stosie zadań, zostanie utworzona nowa. Najważniejsza rzecz, o której należy wspomnieć - drugi parametr PendingIntent.getActivity (), tj. RequestCode powinien mieć wartość niezerową (nazwałem go nawet NON_ZERO_REQUEST_CODE w moim fragmencie), w przeciwnym razie te flagi intencji nie będą działać. Nie mam pojęcia, dlaczego tak jest. Flaga FLAG_ACTIVITY_SINGLE_TOP jest wymienna z androidem: launchMode = "singleTop" w tagu aktywności w manifeście.

Andrey Koretskyy
źródło
Walczyłem od wielu godzin z tymi flagami i zastanawiałem się, dlaczego zmiana moich flag intencji niczego nie zmienia. Niezerowy kod żądania ... niezwykle ważny punkt, o którym należy wspomnieć !!! Działa teraz! Dziękuję Ci!
Max Power
9

Wiem, że jest stary, ale nic z powyższego nie pasowało do mojej aplikacji.

Bez zmiany manifestów i innej konfiguracji, oto kod, który przeniesie Twoją aplikację z powrotem na wierzch - lub otworzy ją po zamknięciu

Intent notificationIntent = context.getPackageManager().getLaunchIntentForPackage(context.getPackageName());
notificationIntent.setPackage(null); // The golden row !!!
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
Raziza O
źródło
To jest prawdziwa odpowiedź!
azizbekian
2
nie, to nie działa - dodaje nowe działanie do bieżącego.
Tobliug
@Tobliug Myślę, że zadziałałoby to, gdybyś spróbował usunąć flagę FLAG_ACTIVITY_NEW_TASK?
rineez
3

Próbowałem tego i zadziałało, mimo że IDE narzekało na kod

Intent notificationIntent = new Intent(THIS_CONTEXT, MainActivity.class);
    notificationIntent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
    PendingIntent intent = PendingIntent.getActivity(THIS_CONTEXT, 0, notificationIntent, Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);

    NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(THIS_CONTEXT)
            .setSmallIcon(R.drawable.cast_ic_notification_0)
            .setContentTitle("Title")
            .setContentText("Content")
            .setContentIntent(intent)
            .setPriority(PRIORITY_HIGH) //private static final PRIORITY_HIGH = 5;
            .setAutoCancel(true)
            /*.setDefaults(Notification.DEFAULT_SOUND | Notification.DEFAULT_VIBRATE | Notification.DEFAULT_LIGHTS)*/;
    NotificationManager mNotificationManager = (NotificationManager) THIS_CONTEXT.getSystemService(Context.NOTIFICATION_SERVICE);
    mNotificationManager.notify(0, mBuilder.build());
nada
źródło
2
<activity android: name = ". MainActivity" android: launchMode = "singleTask">
nada
Intent.FLAG_ACTIVITY_REORDER_TO_FRONTnie jest poprawną flagą dlaPendingINtent.getActivity
rds
@rds Czy możesz połączyć źródło?
Louis CAD
@LouisCAD Przepraszam, nie mam już dostępu do kodu źródłowego
nada
@rds Nie proszę o kod źródłowy, ale o link, w którym czytasz, że Intent.FLAG_ACTIVITY_REORDER_TO_FRONTnie jest to poprawna flaga, gdy jest używana jako PendingIntent. A może chodziło Ci o to, że nie można go przekazać do getActivitymetody, ale można go użyć jako Intentflagi, która jest następnie używana jako PendingIntent?
Louis CAD,
2

Ponieważ mówisz, że chcesz rozpocząć swoją aktywność, jeśli jeszcze się nie rozpoczęła, może nie miałbyś nic przeciwko wznowieniu jej. Przetestowałem również mnóstwo sugestii i kombinacji flag pod kątem celu, to zawsze spowoduje, że aktywność, której potrzebujesz, znajdzie się na pierwszym planie, chociaż nie zachowa żadnego stanu wcześniej z nim związanego.

intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK|Intent.FLAG_ACTIVITY_NEW_TASK);

Tylko API11 +.

Gruby Shogun
źródło
0

Po dodaniu powiadomień miałem podobny problem, jak na stronie szkoleniowej Androida . Żadna z pozostałych odpowiedzi nie zadziałała dla mnie, jednak ta odpowiedź zadziałała dla mnie. Podsumowanie:

final Intent notificationIntent = new Intent(context, YourActivity.class);
notificationIntent.setAction(Intent.ACTION_MAIN);
notificationIntent.addCategory(Intent.CATEGORY_LAUNCHER);
notificationIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
gattsbr
źródło