Como verificar se o AlarmManager já possui um alarme definido?

231

Quando meu aplicativo é iniciado, quero verificar se um alarme específico (registrado pelo AlarmManager) já está definido e em execução. Os resultados do google parecem indicar que não há como fazer isso. Isso ainda está correto? Preciso fazer essa verificação para aconselhar o usuário antes que qualquer ação seja tomada para criar um novo alarme.

Ron
fonte
4
Valide a resposta que resolveu seu problema ou publique sua própria solução.
Anis

Respostas:

322

Após o comentário publicado por Ron, aqui está a solução detalhada. Digamos que você registrou um alarme repetido com uma intenção pendente como esta:

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

A maneira como você verificaria se está ativo é:

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

A chave aqui é a FLAG_NO_CREATEque, conforme descrito no javadoc: if the described PendingIntent **does not** already exists, then simply return null(em vez de criar um novo)

Chris Knight
fonte
9
Ele precisa usar o Intent com apenas uma String de Ação? Tentei especificar uma classe, new Intent (context, MyClass.class), mas parece que não funciona. Ele sempre retorna nulo mesmo quando o alarme está em execução.
toc777
5
toc777, não precisa de ser uma String que corresponde a uma ação declarada em sua intenção filtro em sua manifest.xml
Chris Knight
4
Chris, foi outro problema que estava causando o meu problema. O I intenção mencionado acima realmente funciona :)
toc777
41
Observe que você precisará chamar ambos alarmManager.cancel(pendingIntent)e pendingIntent.cancel()para que esta solução retorne false.
Kevin Cooper
26
Caso isso não seja óbvio, o código nesta resposta não verifica se a intenção pendente foi registrada no gerenciador de alarmes. O código simplesmente verifica se o PendingIntent foi criado via getBroadcast com uma intenção de destino equivalente. Você pode provar isso executando o código alarmUp após todo o getBroadcast, mas antes de todo o material do gerenciador de calendário e alarme. Voltará verdadeiro. Esse fato explica por que você precisa PendingIntent.cancel para obter o valor para retornar a false. A rigor, isso não responde à pergunta.
bigh_29
114

Para outras pessoas que possam precisar disso, aqui está uma resposta.

Usar adb shell dumpsys alarm

Você pode saber que o alarme foi definido e quando eles vão alarmar e intervalo. Também quantas vezes esse alarme foi chamado.

Jack Feng
fonte
36
Não é realmente uma resposta programática para o OP, mas uma dica legal. Muito bom saber.
JustSomeGuy
2
anexar um grep para filtrar a geralmente longa lista de alarmes: adb shell dumpsys alarm | grep <e.g. package name of your app>Também funciona em novos sistemas Windows (eu uso Win10)
muetzenflo
3
O grep é executado no dispositivo móvel, não no seu PC. Portanto, se o grep funcionar, depende do sistema operacional Android. Telefones mais antigos não vêm com grep.
Henning
53

Exemplo de trabalho com o receptor (a resposta principal foi apenas com ação).

//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...");

Vale mencionar:

Se o aplicativo de criação (processo) recuperar novamente o mesmo tipo de PendingIntent (a mesma operação , a mesma Intent - ação, dados, categorias, componentes, sinalizadores ), ele receberá um PendingIntent representando o mesmo token, se isso ainda for válido, e pode chamar cancel () para removê-lo.

Em resumo, seu PendingIntent deve ter os mesmos recursos (estrutura de operação e intenção) para assumir o controle sobre ele.

peixe morto
fonte
1
Não tenho certeza de que isso seja suficiente. No caso em que um PendingIntent é registrado no AlarmManager e, em seguida, interrompido pelos dois métodos de cancelamento, 'isWorking' acima ainda será verdadeiro. O PendingIntent parece não ter sido removido do AlarmManager e continuará retornando uma instância. Como sabemos efetivamente quando os alarmes foram ativados / desativados?
johnDisplayClass
Isso realmente funcionou perfeitamente. Pontos a serem observados: o setAction () e o requestCode () precisam ser idênticos em todos os getBroadcast () e vale a pena desinstalar o aplicativo do seu dispositivo. Isso me pegou de surpresa. Graças
johnDisplayClass
Funciona bem. Obrigado!
Ambran
1
Bom exemplo, mas eu não usaria 1001 como código de solicitação privada lá. Apenas 0 para tornar o exemplo mais óbvio.
Chris
1
Por favor, evite usar a "resposta superior" etc. Forneça um link para a resposta. Porque as respostas podem mudar de posição na página com base na popularidade.
precisa
44

Observe esta citação dos documentos para o método definido do Gerenciador de alarmes:

Se já houver um alarme para este Intent agendado (com a igualdade de dois intentos sendo definida por Intent.filterEquals), ele será removido e substituído por este.

Se você sabe que deseja definir o alarme, não precisa se preocupar em verificar se ele já existe ou não. Basta criá-lo sempre que seu aplicativo inicializar. Você substituirá todos os alarmes passados ​​pelos mesmos Intent.

Você precisa de uma abordagem diferente se estiver tentando calcular quanto tempo resta em um alarme criado anteriormente ou se realmente precisa saber se esse alarme existe. Para responder a essas perguntas, considere salvar dados pref compartilhados no momento em que você criou o alarme. Você pode armazenar o registro de data e hora do relógio no momento em que o alarme foi definido, a hora em que espera que o alarme seja disparado e o período de repetição (se você configurar um alarme repetido).

bigh_29
fonte
2
Na minha opinião, essa deve ser a resposta aceita. A menos que o OP tenha uma situação especial que justifique a não reafirmação do alarme #
2119 Jose_GD
No meu caso, quero saber se o alarme já está definido e, em caso afirmativo, não quero criar um novo ou redefinir o alarme existente.
Imran Aslam
2
Resposta soberba. Por que o OP não verificou isso? Não há nada que você precise fazer.
Vijay Kumar Kanta
2
Existem muitas falhas nesta solução, que podem substituir a hora do alarme criada anteriormente (digamos se a hora precisa ser especificada como t + 24), portanto, toda vez que o aplicativo é iniciado, a hora do alarme continua avançando para um estado que nunca poderia ser alcançado. gatilho para muitos, por isso verificar o alarme se o seu já existe é um mais confiável
Naga
10

Eu tenho 2 alarmes. Estou usando intenção com extras em vez de ação para identificar os eventos:

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

o fato é que, com diff extras, a intenção (e o alarme) não serão únicos. Então, para identificar qual alarme está ativo ou não, tive que definir diff requestCode-s:

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

e aqui está como o alarme foi criado:

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
fonte
Usando os extras de intenção e esta solução funcionou para mim. Apenas uma mudança é que eu estou usando o serviço para que eu tenha alterado paraPendingIntent.getService
Pankaj
8

Acabei de encontrar outra solução, parece funcionar para mim

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);
    }
user3856297
fonte
Às vezes, no Marshmallow, após você forçar a parada de um aplicativo, o getBroadcast () retornará não nulo, mas o alarme não está definido.
hopia
6

Embora quase todo mundo aqui tenha dado a resposta correta, nenhum organismo explicou em que base os Alarmes funcionam

Você pode realmente aprender mais sobre AlarmManagere sobre como está funcionando aqui . Mas aqui está a resposta rápida

Você vê AlarmManagerbasicamente programações PendingIntentem algum momento no futuro. Portanto, para cancelar o alarme programado, você precisa cancelar o PendingIntent.

Sempre anote duas coisas ao criar o arquivo PendingIntent

PendingIntent.getBroadcast(context,REQUEST_CODE,intent, PendingIntent.FLAG_UPDATE_CURRENT);
  • Código de solicitação - atua como o identificador exclusivo
  • Flag - Define o comportamento de PendingIntent

Agora, para verificar se o alarme já está agendado ou para cancelar o alarme, você só precisa ter acesso ao mesmo PendingIntent. Isso pode ser feito se você usar o mesmo código de solicitação e usar FLAG_NO_CREATEcomo mostrado abaixo

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

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

Com FLAG_NO_CREATEele retornará nullse o PendingIntentjá não existir. Se ele já existir, retornará referência ao existentePendingIntent

IrshadKumail
fonte
Se o Código de Solicitação for um identificador, é importante passar o Intent com a Ação correspondente?
precisa saber é o seguinte
Existe uma maneira de obter a hora em que o alarme foi agendado com o gerenciador de alarmes, se você tiver a intenção pendente?
M. Smith
4

Eu criei um script bash simples (estúpido ou não), que extrai os longos do shell adb, os converte em carimbos de data e hora e mostra em vermelho.

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

tente ;)

Jan Ni
fonte
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 não é criar intenção pendente, de modo a fornecer um valor booleano falso.

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

Depois que o AlarmManager verifica o valor de Intenção pendente, ele é verdadeiro porque o AlarmManager atualiza o sinalizador de Intenção pendente.

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

            }
MIkka Marmik
fonte
0

Estou com a impressão de que não há como fazer isso, seria bom.

Você pode obter um resultado semelhante tendo um Alarm_last_set_time gravado em algum lugar e tendo um On_boot_starter BroadcastReciever: BOOT_COMPLETED meio que.

Stephen
fonte