252

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

4

11 回答 11

332

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

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

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

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

于 2012-03-05T23:14:51.640 回答
123

对于可能需要此功能的其他人,这是一个答案。

利用adb shell dumpsys alarm

您可以知道警报已设置,它们何时会发出警报和间隔。还有这个警报被调用了多少次。

于 2014-07-31T03:37:45.737 回答
61

接收器的工作示例(最佳答案只是采取行动)。

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

值得一提的是:

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

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

于 2015-01-21T20:23:40.280 回答
50

请注意文档中有关警报管理器的 set 方法的引用:

如果这个 Intent 调度已经有一个警报(两个 Intent 的相等性由 Intent.filterEquals 定义),那么它将被删除并替换为这个。

如果您知道要设置警报,则无需费心检查它是否已经存在。只需在您的应用程序每次启动时创建它。您将用相同的 替换任何过去的警报Intent

如果您尝试计算之前创建的警报还剩多少时间,或者您确实需要知道此类警报是否存在,则需要采用不同的方法。要回答这些问题,请考虑在创建警报时保存共享首选项数据。您可以存储设置闹钟时的时钟时间戳、您希望闹钟响起的时间以及重复周期(如果您设置了重复闹钟)。

于 2015-04-21T21:31:58.260 回答
11

我有2个闹钟。我正在使用带有附加功能的意图而不是操作来识别事件:

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

问题是,使用 diff extras 的意图(和警报)不会是唯一的。因此,为了能够识别哪个警报处于活动状态,我必须定义 diff requestCode-s:

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

以下是警报的创建方式:

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);
于 2013-02-24T00:06:24.543 回答
9

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

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);
    }
于 2016-07-25T23:18:22.473 回答
9

虽然几乎这里的每个人都给出了正确的答案,但没有人解释警报的工作原理是什么

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

AlarmManager基本上PendingIntent会在将来的某个时间看到时间表。因此,为了取消预定的闹钟,您需要取消PendingIntent.

在创建时始终注意两件事PendingIntent

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

现在要检查是否已经安排了警报或取消警报,您只需要访问相同的PendingIntent. FLAG_NO_CREATE如果您使用相同的请求代码并使用如下所示,则可以做到这一点

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

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

如果不存在,FLAG_NO_CREATE它将返回。如果它已经存在,则返回对现有的引用nullPendingIntentPendingIntent

于 2018-05-06T05:31:34.837 回答
5

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

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

试试看 ;)

于 2015-06-22T14:38:10.703 回答
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 不创建待处理的意图,因此它给出布尔值 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);

在 AlarmManager 检查 Pending Intent 的值后,它给出了 true,因为 AlarmManager 更新了 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");

            }
于 2015-08-19T13:07:13.440 回答
1

根据我的经验,在更新的 Android 版本中,我相信它只允许广播消息以唤醒警报,而不是直接启动服务。请参阅此链接: https ://developer.android.com/training/scheduling/alarms ,它说:

警报具有以下特点:

  • 它们让您可以在设定的时间和/或间隔内触发 Intent。
  • 您可以将它们与广播接收器结合使用来启动服务并执行其他操作。

第二句中的关键词是“连词”。它明确指出的是警报是为广播设计的(这意味着不直接启动服务)。我尝试了几个小时来使用带有 getService() 的 PendingIntent,但无法让它工作,即使我确认挂起的意图可以正常工作,只需使用:

pendingIntent.send(0);

对于“targetSdkVersion 29”,这不起作用.. [不会触发 onStartCommand()]:

Intent launchIntent = new Intent(context, MyService.class);
launchIntent.putExtra(Type.KEY, SERVER_QUERY);    
PendingIntent pendingIntent =
                PendingIntent.getService(context, 0, launchIntent, 0);

我可以使用以下方法验证警报是否正在运行:

adb shell dumpsys alarm | grep com.myapp

但是,这确实有效:

public static class AlarmReceiverWakeup extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        Log.i(TAG, "onReceive Alarm wakeup");
        startService(context);
    }
}


public static void scheduleAlarmWakeup(Context context) {

    Intent broadcastIntent = new Intent(context, AlarmReceiverWakeup.class);
    broadcastIntent.putExtra(Type.KEY, SERVER_QUERY);    
    PendingIntent pendingIntent =
                    PendingIntent.getBroadcast(context, 0, broadcastIntent, 0);
    AlarmManager alarmManager =
                (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);

    // NOTE: using System.currentTimeMillis() fails w/ELAPSED_REALTIME_WAKEUP 
    //     use SystemClock.elapsedRealtime() instead
    alarmManager.setRepeating(
                AlarmManager.ELAPSED_REALTIME_WAKEUP,
                SystemClock.elapsedRealtime()+5000,
                AlarmManager.INTERVAL_FIFTEEN_MINUTES/4,
                getAlarmPendingIntent(context)
    );
}

顺便说一句,这是广播接收器的 AndroidManifest.xml 条目:

<receiver android:name=".ServerQueryService$AlarmReceiverWakeup"
    android:enabled="true">
    <intent-filter>
        <category android:name="android.intent.category.DEFAULT"/>
    </intent-filter>
</receiver>
于 2021-01-09T23:48:18.463 回答
0

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

您可以通过在某处记录 Alarm_last_set_time 和 On_boot_starter BroadcastReciever:BOOT_COMPLETED 来获得类似的结果。

于 2011-01-31T01:34:43.657 回答