如何检查AlarmManager是否已设置闹钟?

ron*_*ron 219 android alarmmanager

当我的应用程序启动时,我希望它检查是否已设置并运行特定警报(通过AlarmManager注册).谷歌的结果似乎表明没有办法做到这一点.这仍然是正确的吗?我需要进行此检查,以便在采取任何操作创建新警报之前通知用户.

Chr*_*ght 311

关注ron发布的评论,这里是详细的解决方案.假设你已经注册了一个具有未决意图的重复警报,如下所示:

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);
Run Code Online (Sandbox Code Playgroud)

您检查它是否处于活动状态的方式是:

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");
}
Run Code Online (Sandbox Code Playgroud)

这里的关键是FLAG_NO_CREATEjavadoc中描述的:( if the described PendingIntent **does not** already exists, then simply return null而不是创建一个新的)

  • 请注意,您需要同时调用```alarmManager.cancel(pendingIntent)```和```pendingIntent.cancel()```以使此解决方案返回false. (38认同)
  • 这种情况并不明显,本答案中的代码不会验证是否已向警报管理器注册了待处理的意图.代码只是验证PendingIntent是通过具有等效目标意图的getBroadcast创建的.您可以通过在getBroadcast all之后运行alarmUp代码来证明这一点,但在所有日历和警报管理器之前.它会返回true.这个事实解释了为什么你必须使用PendingIntent.cancel来获取返回false的值.严格来说,这并没有回答这个问题. (26认同)
  • 它是否必须只使用一个Action String?我尝试指定一个类,新的Intent(context,MyClass.class),但它似乎不起作用.即使警报正在运行,它也始终返回null. (9认同)
  • toc777,不需要是一个String,它匹配manifest.xml中intent-filter中声明的操作 (5认同)
  • 克里斯,这是导致我的问题的另一个问题.我上面提到的意图确实有效:) (4认同)
  • 这是我第一次看到如此高评价的错误答案.请参阅bigh_29的评论. (4认同)
  • 打扰一下。我想问一下什么是 MY_UNIQUE_ACTION ?它来自哪里? (2认同)
  • 我试过Intent intent = new Intent(ctx,MyReceiver.class); 而不是传递unique_action,它工作:) (2认同)

Jac*_*eng 109

对于可能需要这个的人来说,这是一个答案.

使用 adb shell dumpsys alarm

您可以知道已经设置了警报以及它们何时会发出警报和间隔.此外,还会调用此警报的次数.

  • 不是OP的程序化答案,而是一个很酷的提示.很高兴知道. (32认同)
  • grep在移动设备上执行,而不是在PC上执行.因此,如果grep工作取决于Android操作系统.较旧的手机没有配备grep. (3认同)
  • 附加一个grep来过滤通常很长的警报列表:`adb shell dumpsys alarm | grep <例如,您的应用程序的软件包名称>`在新的Windows系统上也可以使用(我使用Win10) (2认同)

dea*_*ish 49

带接收器的工作示例(最重要的答案就是行动).

//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...");
Run Code Online (Sandbox Code Playgroud)

值得一提的是:

如果创建应用程序稍后(进程)重新检索相同类型的PendingIntent(相同的操作,相同的Intent - 动作,数据,类别,组件,标志),它将接收表示同一令牌的PendingIntent,如果它仍然有效,并且因此可以调用cancel()来删除它.

简而言之,您的PendingIntent应该具有相同的功能(操作和意图的结构)来控制它.

  • 我不确定这是否足够。如果 PendingIntent 注册到 AlarmManager 然后被两个取消方法停止,上面的 'isWorking' 仍然为真。PendingIntent 似乎还没有从 AlarmManager 中删除,并将继续返回一个实例。那么我们如何有效地知道何时打开/关闭警报? (3认同)
  • 请不要使用“最佳答案”等。而是提供指向答案的链接。因为答案可以根据受欢迎程度改变页面上的位置。 (2认同)

big*_*_29 44

请注意警报管理器的文档中的引用:

如果已经安排了此Intent的警报(Intent.filterEquals定义了两个意图相等),那么它将被删除并替换为此.

如果您正在尝试决定是否创建警报,请不要打扰询问警报是否存在.只需在每次应用启动时创建它.您将替换您配置的任何过去警报.

如果您正在尝试计算先前创建的警报剩余时间,或者您确实需要知道此警报是否存在,则需要采用不同的方法.要回答这些问题,请考虑在创建警报时保存共享的pref数据.您可以在设置闹钟时存储时钟时间戳,预计闹钟关闭的时间以及重复周期(如果您设置了重复闹钟).

  • 我认为这应该是公认的答案。除非OP有特殊情况证明不重发警报 (2认同)
  • 极好的答案。OP为什么根本不检查这一点?您什么都不需要做。 (2认同)
  • 这个解决方案有很多漏洞,这可能会覆盖之前创建的警报时间(假设时间需要像 t+24 一样指定)所以每次应用程序启动时,警报时间都会向前移动一个它永远无法获得的状态触发许多,因此检查警报是否已经存在是更可靠的一种 (2认同)

HiB*_*HiB 10

我有2个闹钟.我使用intent而不是action来识别事件:

Intent i = new Intent(context, AppReciever.class);
i.putExtra("timer", "timer1");
Run Code Online (Sandbox Code Playgroud)

事实上,对于diff extras,意图(和警报)不会是唯一的.因此,为了能够识别哪个警报是活动的,我必须定义diff requestCode-s:

boolean alarmUp = (PendingIntent.getBroadcast(context, MyApp.TIMER_1, i, 
                    PendingIntent.FLAG_NO_CREATE) != null);
Run Code Online (Sandbox Code Playgroud)

以下是警报的创建方式:

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);
Run Code Online (Sandbox Code Playgroud)


小智 7

刚刚找到另一种解决方案,它似乎对我有用

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);
    }
Run Code Online (Sandbox Code Playgroud)


nir*_*997 7

If you target Android 12 (i.e) Target SDK 31, then, for AlarmManager, PendingIntent cannot be created without a Mutable or an Immutable flag. Without this Mutability flag, the application will throw a runtime error. See this documentation for more details about this. The following code snippet is working for me and will help fellow people who target their application for Android 12.

For creating an Alarm:

public static void setupReminderServiceAlarm ( Context context ) {
    Log.d ( TAG, "Trying to setup reminder service alarm" );
    if (!isReminderServiceAlarmSet ( context )) {
        AlarmManager alarmManager = (AlarmManager) context.getApplicationContext ().getSystemService ( Context.ALARM_SERVICE );
        Intent intent = new Intent ( context.getApplicationContext (), ReminderIntentReceiver.class );
        intent.setAction ( REMINDER_INTENT_ACTION );
        PendingIntent pendingIntent;
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) {
            pendingIntent = PendingIntent.getBroadcast ( context.getApplicationContext (), REMINDER_INTENT_REQUEST_CODE, intent, PendingIntent.FLAG_IMMUTABLE );
        } else {
            pendingIntent = PendingIntent.getBroadcast ( context.getApplicationContext (), REMINDER_INTENT_REQUEST_CODE, intent, 0 );
        }
        alarmManager.setRepeating ( AlarmManager.RTC_WAKEUP, getReminderTriggerTime (), REMINDER_INTERVAL, pendingIntent );
        Log.d ( TAG, "Reminder service alarm setup completed" );
    }
}
Run Code Online (Sandbox Code Playgroud)

For checking whether the alarm is already set or not:

private static boolean isReminderServiceAlarmSet ( Context context ) {
    Intent intent = new Intent ( context.getApplicationContext (), ReminderIntentReceiver.class );
    intent.setAction ( REMINDER_INTENT_ACTION );
    boolean isBackupServiceAlarmSet;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
        PendingIntent.getBroadcast ( context.getApplicationContext (), REMINDER_INTENT_REQUEST_CODE, intent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_NO_CREATE );
        isBackupServiceAlarmSet = (PendingIntent.getBroadcast ( context.getApplicationContext (), REMINDER_INTENT_REQUEST_CODE, intent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_NO_CREATE ) != null);
    } else {
        PendingIntent.getBroadcast ( context.getApplicationContext (), REMINDER_INTENT_REQUEST_CODE, intent, PendingIntent.FLAG_NO_CREATE );
        isBackupServiceAlarmSet = (PendingIntent.getBroadcast ( context.getApplicationContext (), REMINDER_INTENT_REQUEST_CODE, intent, PendingIntent.FLAG_NO_CREATE ) != null);
    }
    Log.d ( TAG, "Reminder service alarm is " + (isBackupServiceAlarmSet ? "" : "not ") + "set already" );
    return isBackupServiceAlarmSet;
}
Run Code Online (Sandbox Code Playgroud)

For cancelling the alarm which was set earlier:

public static void cancelReminderServiceAlarm ( Context context ) {
    Log.d ( TAG, "Reminder service alarm canceled" );
    AlarmManager alarmManager = (AlarmManager) context.getApplicationContext ().getSystemService ( Context.ALARM_SERVICE );
    Intent intent = new Intent ( context.getApplicationContext (), ReminderIntentReceiver.class );
    intent.setAction ( REMINDER_INTENT_ACTION );
    PendingIntent pendingIntent;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
        pendingIntent = PendingIntent.getBroadcast ( context.getApplicationContext (), REMINDER_INTENT_REQUEST_CODE, intent, PendingIntent.FLAG_IMMUTABLE );
    } else {
        pendingIntent = PendingIntent.getBroadcast ( context.getApplicationContext (), REMINDER_INTENT_REQUEST_CODE, intent, 0 );
    }
    alarmManager.cancel ( pendingIntent );
    pendingIntent.cancel ();
}
Run Code Online (Sandbox Code Playgroud)

Hope this answer helps people who target their application for Android 12 / SDK 31+. Also, make sure to add this permission in Manifest for scheduling alarms targeting the above.

<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"/>
Run Code Online (Sandbox Code Playgroud)


小智 5

我制作了一个简单(愚蠢与否)的 bash 脚本,它从 adb shell 中提取 longs,将它们转换为时间戳并以红色显示。

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;)
Run Code Online (Sandbox Code Playgroud)

尝试一下 ;)


Irs*_*ail 5

尽管这里的几乎每个人都给出了正确的答案,但是没有人解释“警报”的依据是什么

您实际上可以AlarmManager这里了解更多信息及其工作原理。但这是快速答案

AlarmManager基本上会看到PendingIntent将来某个时间的时间表。因此,要取消预定的警报,您需要取消PendingIntent

创建时始终注意两件事 PendingIntent

PendingIntent.getBroadcast(context,REQUEST_CODE,intent, PendingIntent.FLAG_UPDATE_CURRENT);
Run Code Online (Sandbox Code Playgroud)
  • 请求代码-充当唯一标识符
  • 标志-定义行为 PendingIntent

现在,要检查警报是否已安排好或要取消警报,您只需要访问该警报即可PendingIntent。如果您使用相同的请求代码并按FLAG_NO_CREATE如下所示使用,则可以完成此操作

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

if (pendingIntent!=null)
   alarmManager.cancel(pendingIntent);
Run Code Online (Sandbox Code Playgroud)

随着FLAG_NO_CREATE它会返回null,如果PendingIntent不存在。如果已经存在,则返回对现有对象的引用PendingIntent


小智 0

我的印象是没有办法做到这一点,但那会很好。

您可以通过在某处记录 Alarm_last_set_time 并使用 On_boot_starter BroadcastReciever:BOOT_COMPLETED 之类的东西来实现类似的结果。