Android 轮询最佳实践 Service + AlarmManager
Android---AlarmManager(全局定时器/闹钟)指定时长或以周期形式执行某项操作
[ANDROID 開發筆記] SCHEDULING REPEATING ALARMS 範例教學 (鬧鐘,定時器)
set(int type, long triggerAtMillis, PendingIntent operation)
该方法用于设置一次性闹钟,第一个参数表示闹钟类型,第二个参数表示闹钟执行时间,第三个参数表示闹钟响应动作。
setRepeating(int type, long triggerAtMillis,long intervalMillis, PendingIntent operation)
该方法用于设置重复闹钟,第一个参数表示闹钟类型,第二个参数表示闹钟首次执行时间,第三个参数表示闹钟两次执行的间隔时间,第四个参数表示闹钟响应动作。
setInexactRepeating(int type, long triggerAtMillis,long intervalMillis, PendingIntent operation)
该方法也用于设置重复闹钟,与第二个方法相似,不过闹钟时间不精确。
setExact(int type, long triggerAtMillis, PendingIntent operation)
setWindow(int type, long windowStartMillis, long windowLengthMillis,PendingIntent operation)
方法1和方法2在SDK_INT 19以前是精确的闹钟,19以后为了节能省电(减少系统唤醒和电池使用)。使用Alarm.set()和Alarm.setRepeating()已经不能保证精确性,不过还好Google又提供了两个精确的Alarm方法setWindow()和setExact(),所以19以后需要精确的闹钟就需要上面两个方法,具体原因后面再说
cancel(PendingIntent operation)
取消Intent相同的闹钟,这里是根据Intent中filterEquals(Intent other)方法来判断是否相同
public boolean filterEquals(Intent other) {
if (other == null) {
return false;
}
if (!Objects.equals(this.mAction, other.mAction)) return false;
if (!Objects.equals(this.mData, other.mData)) return false;
if (!Objects.equals(this.mType, other.mType)) return false;
if (!Objects.equals(this.mPackage, other.mPackage)) return false;
if (!Objects.equals(this.mComponent, other.mComponent)) return false;
if (!Objects.equals(this.mCategories, other.mCategories)) return false;
return true;
}
从方法体可以看出mAction、mData、mType、mPackage、mComponent、mCategories这几个完全一样就认定为同一Intent
闹钟类型
这个闹钟类型就是前面setxxx()方法第一个参数int type.
- AlarmManager.ELAPSED_REALTIME :使用相对时间,可以通过SystemClock.elapsedRealtime() 获取(从开机到现在的毫秒数,包括手机的睡眠时间),设备休眠时并不会唤醒设备。
- AlarmManager.ELAPSED_REALTIME_WAKEUP :与ELAPSED_REALTIME基本功能一样,只是会在设备休眠时唤醒设备。
- AlarmManager.RTC :使用绝对时间,可以通过 System.currentTimeMillis()获取,设备休眠时并不会唤醒设备。
- AlarmManager.RTC_WAKEUP : 与RTC基本功能一样,只是会在设备休眠时唤醒设备。
FAQQ1. 设定多个定时闹钟为什么只有最后一个生效
A: AlarmManager 根据 PendingIntent requestCode 来判断是否是同一个定时服务,所以当
requestCode相等的时候只有最后一个生效
1. 我们说的requestCode 是第二个参数
PendingIntent.getBroadcast(Context context, int requestCode, Intent intent, @Flags int flags)
2. 例如:
PendingIntent pi1 = PendingIntent.getBroadcast(this, 1, intent, 0);
AlarmManager.setRepeating(AlarmManager.RTC_WAKEUP, startTime, intervalTime, pi1);
PendingIntent pi2 = PendingIntent.getBroadcast(this, 2, intent, 0);
AlarmManager.setRepeating(AlarmManager.RTC_WAKEUP, startTime, intervalTime, pi2);
这个样子就可以设定2个了
AlarmManager 最少只能每5秒执行一次操作,所以不能每秒钟都更新一次,如果需要显示秒数,可以使用 handle 的 postDelay 方法来实现
最后一个建议:
在调用setRepeating安排定时器之前,最好检查定时器是否已经在运行,像这样:
if (isServiceAlarmOn(this)) {
alarmManager.setRepeating(AlarmManager.RTC,
System.currentTimeMillis(), POLL_INTERVAL, pi);
} else {
// 取消Alarm,同时取消PendingIntent.
alarmManager.cancel(pi);
pi.cancel();
}
// 由于在取消Alarm的同时也取消了pi,并且一个PendingIntent只能登记给一个Alarm,
// 所以可通过检查pi是否存在,来确认Alarm是否激活。
public static boolean isServiceAlarmOn(Context context) {
Intent i = new Intent(context, PollService.class);
// FLAG_NO_CREATE表示如果描述的pi不存在,则返回null,而不是创建它。
PendingIntent pi = PendingIntent.getService(
context, 0, i, PendingIntent.FLAG_NO_CREATE);
return pi != null;
}