Jak sprawdzić, czy AlarmManager ma już ustawiony alarm?

231

Po uruchomieniu mojej aplikacji chcę, aby sprawdził, czy określony alarm (zarejestrowany przez AlarmManager) jest już ustawiony i działa. Wyniki z Google wydają się wskazywać, że nie ma na to sposobu. Czy to nadal jest poprawne? Muszę to sprawdzić, aby poinformować użytkownika przed podjęciem jakichkolwiek działań w celu utworzenia nowego alarmu.

Ron
źródło
4
Sprawdź poprawność odpowiedzi, która rozwiązała problem, lub opublikuj własne rozwiązanie.
Anis,

Odpowiedzi:

322

W odpowiedzi na opublikowany komentarz, oto szczegółowe rozwiązanie. Załóżmy, że zarejestrowałeś powtarzający się alarm z takim zamiarem:

Intent intent = new Intent("com.my.package.MY_UNIQUE_ACTION");
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, 
                                      intent, PendingIntent.FLAG_UPDATE_CURRENT);
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.add(Calendar.MINUTE, 1);

AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), 1000 * 60, pendingIntent);

Aby sprawdzić, czy jest aktywny, możesz:

boolean alarmUp = (PendingIntent.getBroadcast(context, 0, 
        new Intent("com.my.package.MY_UNIQUE_ACTION"), 
        PendingIntent.FLAG_NO_CREATE) != null);

if (alarmUp)
{
    Log.d("myTag", "Alarm is already active");
}

Kluczem tutaj jest to, FLAG_NO_CREATEco opisano w javadoc: if the described PendingIntent **does not** already exists, then simply return null(zamiast tworzenia nowego)

Chris Knight
źródło
9
Czy musi używać zamiaru za pomocą ciągu akcji? Próbowałem określić klasę, nową intencję (kontekst, MyClass.class), ale wydaje się, że nie działa. Zawsze zwraca zero, nawet gdy alarm jest uruchomiony.
toc777
5
toc777, nie, to nie musi być ciąg znaków, który pasuje do zadeklarowanej akcji w filtrze intencji w pliku manifestu. xml
Chris Knight
4
Chris, to był kolejny problem, który spowodował mój problem. Cel, o którym wspomniałem powyżej, faktycznie działa :)
toc777
41
Zauważ, że musisz zadzwonić zarówno alarmManager.cancel(pendingIntent)a pendingIntent.cancel()celem tego rozwiązania return false.
Kevin Cooper
26
W przypadku, gdy nie jest to oczywiste, kod w tej odpowiedzi nie weryfikuje, czy oczekująca zamiar została zarejestrowana w menedżerze alarmów. Kod po prostu sprawdza, czy obiekt PendingIntent został utworzony za pośrednictwem getBroadcast z równoważnym celem docelowym. Możesz to udowodnić, uruchamiając kod alarmUp po getBroadcast all, ale przed wszystkimi informacjami dotyczącymi kalendarza i menedżera alarmów. To zwróci prawdę. Ten fakt wyjaśnia, dlaczego musisz PendingIntent.cancel, aby wartość powróciła do wartości false. Ściśle mówiąc, to nie odpowiada na pytanie.
bigh_29
114

Dla innych, którzy mogą tego potrzebować, oto odpowiedź.

Posługiwać się adb shell dumpsys alarm

Możesz wiedzieć, że alarm został ustawiony i kiedy będą alarmowane i interwały. Również ile razy ten alarm był wywoływany.

Jack Feng
źródło
36
Nie tak naprawdę programowa odpowiedź na PO, ale fajna wskazówka. Bardzo dobrze wiedzieć.
JustSomeGuy
2
dodaj grep, aby przefiltrować zwykle długą listę alarmów: adb shell dumpsys alarm | grep <e.g. package name of your app>Działa również na nowych systemach Windows (używam Win10)
muetzenflo
3
grep jest wykonywany na urządzeniu mobilnym, a nie na komputerze. Więc jeśli grep działa, zależy od systemu operacyjnego Android. Starsze telefony nie mają grep.
Henning,
53

Przykład działania z odbiornikiem (najlepsza odpowiedź dotyczyła tylko akcji).

//starting
AlarmManager alarmManager = (AlarmManager) getActivity().getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(getActivity(), MyReceiver.class);
intent.setAction(MyReceiver.ACTION_ALARM_RECEIVER);//my custom string action name
PendingIntent pendingIntent = PendingIntent.getBroadcast(getActivity(), 1001, intent, PendingIntent.FLAG_CANCEL_CURRENT);//used unique ID as 1001
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), aroundInterval, pendingIntent);//first start will start asap

//and stopping
Intent intent = new Intent(getActivity(), MyReceiver.class);//the same as up
intent.setAction(MyReceiver.ACTION_ALARM_RECEIVER);//the same as up
PendingIntent pendingIntent = PendingIntent.getBroadcast(getActivity(), 1001, intent, PendingIntent.FLAG_CANCEL_CURRENT);//the same as up
alarmManager.cancel(pendingIntent);//important
pendingIntent.cancel();//important

//checking if alarm is working with pendingIntent
Intent intent = new Intent(getActivity(), MyReceiver.class);//the same as up
intent.setAction(MyReceiver.ACTION_ALARM_RECEIVER);//the same as up
boolean isWorking = (PendingIntent.getBroadcast(getActivity(), 1001, intent, PendingIntent.FLAG_NO_CREATE) != null);//just changed the flag
Log.d(TAG, "alarm is " + (isWorking ? "" : "not") + " working...");

Warto wspomnieć:

Jeśli aplikacja tworząca później (proces) ponownie pobierze ten sam rodzaj PendingIntent (ta sama operacja , ten sam Intent - działanie, dane, kategorie, komponenty, flagi ), otrzyma PendingIntent reprezentujący ten sam token, jeśli jest nadal ważny, i może w ten sposób wywołać cancel (), aby go usunąć.

Krótko mówiąc, Twój obiekt PendingIntent powinien mieć te same funkcje (działanie i struktura intencji), aby przejąć nad nim kontrolę.

martwa ryba
źródło
1
Nie jestem pewien, czy to wystarczy. W przypadku zarejestrowania PendingIntent w AlarmManager, a następnie zatrzymania za pomocą obu metod anulowania, powyższe „isWorking” będzie nadal prawdziwe. Wygląda na to, że obiekt PendingIntent nie został usunięty z AlarmManager i będzie nadal zwracał instancję. Skąd zatem wiemy, kiedy alarmy zostały włączone / wyłączone?
johnDisplayClass
To faktycznie działało idealnie. Warto pamiętać: setAction () i requestCode () muszą być identyczne we wszystkich getBroadcast () i warto odinstalować aplikację z urządzenia. To mnie złapało. Dzięki
johnDisplayClass
Działa świetnie. Dzięki!
Ambran
1
Dobry przykład, ale nie użyłbym tam 1001 jako prywatnego kodu żądania. Tylko 0, aby uczynić przykład bardziej oczywistym.
Chris
1
Powstrzymaj się od używania „najwyższej odpowiedzi” itp. Zamiast tego podaj link do odpowiedzi. Ponieważ odpowiedzi mogą zmieniać pozycje na stronie na podstawie popularności.
Kathir
44

Zwróć uwagę na ten cytat z dokumentacji dla ustawionej metody Alarm Managera:

Jeśli jest już zaplanowany alarm dla tej intencji (przy równej wartości dwóch intencji zdefiniowanej przez Intent.filterEquals), zostanie on usunięty i zastąpiony tym.

Jeśli wiesz, że chcesz ustawić alarm, nie musisz zawracać sobie głowy sprawdzaniem, czy już istnieje. Po prostu utwórz go przy każdym uruchomieniu aplikacji. Wszystkie wcześniejsze alarmy zastąpisz tym samym Intent.

Potrzebujesz innego podejścia, jeśli próbujesz obliczyć, ile czasu pozostało do wcześniej utworzonego alarmu lub jeśli naprawdę potrzebujesz wiedzieć, czy taki alarm w ogóle istnieje. Aby odpowiedzieć na te pytania, rozważ zapisanie udostępnionych danych wstępnych w momencie tworzenia alarmu. Możesz zapisać znacznik czasu w momencie ustawienia alarmu, oczekiwany czas wyłączenia alarmu oraz okres powtarzania (jeśli ustawisz alarm powtarzalny).

bigh_29
źródło
2
Moim zdaniem powinna to być zaakceptowana odpowiedź. Chyba że OP ma specjalną sytuację, która uzasadnia nie
przywrócenie
W moim przypadku chcę wiedzieć, czy alarm jest już ustawiony, a jeśli tak, nie chcę tworzyć nowego ani resetować istniejącego alarmu.
Imran Aslam
2
Znakomita odpowiedź. Dlaczego OP w ogóle tego nie sprawdził? Nie musisz nic robić.
Vijay Kumar Kanta
2
W tym rozwiązaniu jest wiele dziur w pętli, może to zastąpić wcześniej utworzony czas alarmu (powiedzmy, jeśli czas należy określić jak t + 24), więc za każdym razem, gdy aplikacja jest uruchamiana, czas alarmu przesuwa się do przodu, którego nigdy nie uzyska wyzwolić dla wielu, więc sprawdzenie alarmu, jeśli już istnieje, jest bardziej niezawodne
Naga
10

Mam 2 alarmy. Używam zamiaru z dodatkami zamiast akcji, aby zidentyfikować zdarzenia:

Intent i = new Intent(context, AppReciever.class);
i.putExtra("timer", "timer1");

Chodzi o to, że w przypadku dodatków różnicowych cel (i alarm) nie będzie unikalny. Aby móc zidentyfikować, który alarm jest aktywny, czy nie, musiałem zdefiniować diff requestCode:

boolean alarmUp = (PendingIntent.getBroadcast(context, MyApp.TIMER_1, i, 
                    PendingIntent.FLAG_NO_CREATE) != null);

a oto jak powstał alarm:

public static final int TIMER_1 = 1;
public static final int TIMER_2 = 2;

PendingIntent pending = PendingIntent.getBroadcast(context, TIMER_1, i,
            PendingIntent.FLAG_CANCEL_CURRENT);
setInexactRepeating(AlarmManager.RTC_WAKEUP,
            cal.getTimeInMillis(), AlarmManager.INTERVAL_DAY, pending);
pending = PendingIntent.getBroadcast(context, TIMER_2, i,
            PendingIntent.FLAG_CANCEL_CURRENT);
setInexactRepeating(AlarmManager.RTC_WAKEUP,
            cal.getTimeInMillis(), AlarmManager.INTERVAL_DAY, pending);
HiB
źródło
Korzystanie z dodatków celowych i tego rozwiązania działało dla mnie. Korzystam tylko z jednej usługi, więc zmieniłem ją naPendingIntent.getService
Pankaj
8

Właśnie znalazłem inne rozwiązanie, wydaje mi się, że działa

Intent myIntent = new Intent(MainActivity.this, MyReceiver.class);

boolean isWorking = (PendingIntent.getBroadcast(MainActivity.this, 0, myIntent, PendingIntent.FLAG_NO_CREATE) != null);
if (isWorking) {Log.d("alarm", "is working");} else {Log.d("alarm", "is not working");}

if(!isWorking) {
    pendingIntent = PendingIntent.getBroadcast(MainActivity.this, 0, myIntent,    PendingIntent.FLAG_UPDATE_CURRENT);
    alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
    int timeNotif = 5 * 60 * 1000;//time in ms, 7*24*60*60*1000 for 1 week
    Log.d("Notif", "Notification every (ms): " + timeNotif);
    alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), timeNotif, pendingIntent);
    }
użytkownik3856297
źródło
Czasami na Marshmallow, po wymuszeniu zatrzymania aplikacji, getBroadcast () zwraca wartość inną niż null, ale alarm nie jest ustawiony.
hopia
6

Chociaż prawie wszyscy tutaj udzielili prawidłowej odpowiedzi, żadne ciało nie wyjaśniło, na jakiej podstawie działają Alarmy

Możesz dowiedzieć się więcej o tym AlarmManageri jak działa tutaj . Ale oto szybka odpowiedź

Widzisz AlarmManagerzasadniczo harmonogramy PendingIntentw przyszłości. Aby więc anulować zaplanowany alarm, musisz anulować PendingIntent.

Zawsze miej na uwadze dwie rzeczy podczas tworzenia PendingIntent

PendingIntent.getBroadcast(context,REQUEST_CODE,intent, PendingIntent.FLAG_UPDATE_CURRENT);
  • Kod żądania - Działa jako unikalny identyfikator
  • Flaga - określa zachowanie PendingIntent

Teraz, aby sprawdzić, czy alarm jest już zaplanowany lub anulować alarm, wystarczy uzyskać do niego dostęp PendingIntent. Można to zrobić, jeśli używasz tego samego kodu żądania i używasz go FLAG_NO_CREATEjak pokazano poniżej

PendingIntent pendingIntent=PendingIntent.getBroadcast(this,REQUEST_CODE,intent,PendingIntent.FLAG_NO_CREATE);

if (pendingIntent!=null)
   alarmManager.cancel(pendingIntent);

Dzięki FLAG_NO_CREATEnie wróci, nulljeśli jeszcze PendingIntentnie istnieje. Jeśli już istnieje, zwraca odwołanie do istniejącegoPendingIntent

IrshadKumail
źródło
Jeśli kod żądania jest identyfikatorem, czy ważne jest, aby przekazać zamiar za pomocą pasującego działania?
Sekula1991
Czy istnieje sposób, aby uzyskać czas, na który alarm został zaplanowany, za pomocą menedżera alarmów, jeśli masz zamiar?
M. Smith
4

Zrobiłem prosty (głupi lub nie) skrypt bashowy, który wyodrębnia te długie z powłoki adb, konwertuje je na znaczniki czasu i pokazuje na czerwono.

echo "Please set a search filter"
read search

adb shell dumpsys alarm | grep $search | (while read i; do echo $i; _DT=$(echo $i | grep -Eo 'when\s+([0-9]{10})' | tr -d '[[:alpha:][:space:]]'); if [ $_DT ]; then echo -e "\e[31m$(date -d @$_DT)\e[0m"; fi; done;)

Spróbuj ;)

Jan Ni
źródło
1
    Intent intent = new Intent("com.my.package.MY_UNIQUE_ACTION");
            PendingIntent pendingIntent = PendingIntent.getBroadcast(
                    sqlitewraper.context, 0, intent,
                    PendingIntent.FLAG_NO_CREATE);

FLAG_NO_CREATE nie jest tworzony w oczekiwaniu, więc podaje wartość logiczną false.

            boolean alarmUp = (PendingIntent.getBroadcast(sqlitewraper.context, 0,
                    new Intent("com.my.package.MY_UNIQUE_ACTION"),
                    PendingIntent.FLAG_NO_CREATE) != null);

            if (alarmUp) {
                System.out.print("k");

            }

            AlarmManager alarmManager = (AlarmManager) sqlitewraper.context
                    .getSystemService(Context.ALARM_SERVICE);
            alarmManager.setRepeating(AlarmManager.RTC_WAKEUP,
                    System.currentTimeMillis(), 1000 * 60, pendingIntent);

Po sprawdzeniu przez AlarmManager wartości Pending Intent, daje wartość true, ponieważ AlarmManager Update The Flag of Pending Intent.

            boolean alarmUp1 = (PendingIntent.getBroadcast(sqlitewraper.context, 0,
                    new Intent("com.my.package.MY_UNIQUE_ACTION"),
                    PendingIntent.FLAG_UPDATE_CURRENT) != null);
            if (alarmUp1) {
                System.out.print("k");

            }
Mekka Marmik
źródło
0

Mam wrażenie, że nie ma takiej możliwości, byłoby jednak miło.

Możesz osiągnąć podobny wynik, nagrywając gdzieś Alarm_last_set_time, i mając coś w rodzaju On_boot_starter BroadcastReciever: BOOT_COMPLETED.

Stephen
źródło