使用 AlarmManager 设置闹钟

在Android开发中,会存在这么些场景 : 你需要在稍后的某个时间点或者执行一个任务,例如:9点通知用户商品开卖;每天8点播放特定形式的闹钟。

AlarmManager 是什么东东?

AlarmManager 提供了对系统的 alarm services 访问的能力,利用它可以使应用程序在将来的某一个时刻启动指定的 Intent,其实质是系统发出了为这个 Intent 注册的广播。接收这个广播并进行处理可以完成闹钟、定时任务等功能。

需要注意的是,即使设备在休眠状态,我们注册的 alarm 还是会保留的。我们可以选择性的设置是否唤醒设备,但是当设备关机和重启后,闹钟将会被清除。

AlarmManger 的常用API

set()  设定一次性警报

setRepeating()  设定重复的警报

cancel() 取消警报

更多API,查看 AlarmManager 官方文档

set(int type, long triggerAtMillis, String tag, AlarmManager.OnAlarmListener listener, Handler targetHandler)

这个方法的第一个参数 type 有以下四种选择:

ELAPSED_REALTIME, ELAPSED_REALTIME_WAKEUP, RTC, RTC_WAKEUP.

带有 WAKEUP 的参数会在睡眠状态下唤醒设备,而不带 WAKEUP 的参数不会唤醒设备,直到下一次设备唤醒时才发出警报;带有RTC的参数,是根据系统时间发出警报,用户调整系统时间可以触发警报。 而带有ELAPSED_REALTIME的参数,是根据设备真实流逝的时间发出警报,用户调整系统时间不会触发警报。

注意:在 API 19 (KITKAT,系统版本 4.4)以后,系统会不会严格按照我们设定时间唤醒设备,而是将唤醒时间相近的任务安排在一起唤醒以节约电量。这意味着我们设定的定时任务不一定能在我们设定的时间上准确 执行,可能提前或者延迟执行。而targetSdkVersion在API 19之前应用仍将继续使用以前的行为,所有的闹钟在要求准确传递的情况下都会准确传递。

如果需要在API 19之前之后要求闹钟在准确的时间执行,可以使用下面API :

setExact() 设置警报在确切的时间点执行

setWindow() 设置警报在确切的时间窗口执行

使用示例

Step 1:获取 AlarmManager 并设置警报

AlarmManager am = (AlarmManager)   getSystemService(ALARM_SERVICE);

//在Intent 设定警报触发后执行的组件
Intent intent = new Intent(MainActivity.this, OneShotAlarm.class);
PendingIntent sender = PendingIntent.getBroadcast(MainActivity.this, 0, intent, 0);
//根据系统版本设置警报,以达到都能确切执行。
 if (Build.VERSION.SDK_INT < 19) {
        am.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), sender);
    } else {
        am.setExact(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), sender);
    }

Step 2:通过广播接收系统发来的通知

public class OneShotAlarm extends BroadcastReceiver {
private static final String TAG = "OneShotAlarm";

@Override
public void onReceive(Context context, Intent intent) {
//  在这里执行我们需要的业务代码
}

注意:当 Android 设备处于休眠状态下,我们的程序代码将被停止执行。而 Alarm Manager持有一个和 alarm onReceive() 方法执行时间一样长的CPU唤醒锁,这样可以保证设备在处理完广播之前不会sleep。一旦 onReceive 方法执行完毕返回,Alarm Manager 就会释放CPU唤醒锁。

如果你的alarm receiver中调用了Context.startService(),那么很可能service还没起来设备就sleep了。为了阻止这种情况,你的BroadcastReceiver和Service需要实现不同的唤醒锁机制,来确保设备持续运行到service可用为止。

可能会遇到的问题

Q:API 19以后如何设置重复闹钟?
A:我们在使用AlarmManager设置了提醒之后,是通过广播接收的,设置的提醒时间一到,系统发送我们自定义的广播,我们接收到,应用程序提醒。那提醒的时候,我们可以再重新设置一次嘛,这就解决了API 19设置重复闹钟的问题。

Q:手机重启之后,闹钟失效怎么解决?
设置一个监听手机重启的广播,等手机重启的时候,再重新设置一遍,即可解决上面的问题。

Q:应用程序被Kill掉后,设置的闹钟不响,怎么办?
A:应用程序被Kill掉后,设置的闹钟失效,可以利用守护进程、灰色等保活手段来保证后台闹钟服务不被Kill掉。当应用程序以及闹钟服务被Kill掉,守护进程以及灰色保活来重新启动闹钟服务,并且重新设置闹钟。保活进程只能保证部分手机能正常,如果业务只要求闹钟功能且要求100%能响应,可以通过添加系统闹钟来完成。

具体查看这篇文章, ,这篇文章还提到 在5.0 利用 JobScheduler 进行保活 和 6.0 以上Doze模式的处理。

总结

AlarmManager 其实质是系统发出了为这个 Intent 注册的广播,接收这个广播并进行处理可以任何定时任务功能而不仅是闹钟功能。

使用 AlarmManager 往往会由于进程被杀而导致不能正常任务,通过一些进程保活手段保持进程存活但并不代表所有的Android手机的定时任务功能都可以用,只能尽最大的可能保证大部分的手机。


参考: