先来一发简单的Demo体验一下:
1、AndroidManifest.xml注册Service服务。
2、利用AlarmManager 3秒后发送Intent
public void alarm() {
AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(this, AlarmService.class);
intent.setAction(ACTION_ALARM);
PendingIntent pendingIntent = PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
if (Build.VERSION.SDK_INT < 19) {
am.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + 3000, pendingIntent);
} else {
am.setExact(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + 3000, pendingIntent);
}
}
3、Service代码
public class AlarmService extends Service {
private Handler mHanler = new Handler(Looper.getMainLooper());
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
mHanler.post(new Runnable() {
@Override
public void run() {
Toast.makeText(AlarmService.this, "闹钟来啦", Toast.LENGTH_SHORT).show();
}
});
return super.onStartCommand(intent, flags, startId);
}
}
具体见Demo:https://github.com/suyimin/Demos
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)
SDK_INT 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.
具体年月日启动闹钟
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.YEAR,2016);
calendar.set(Calendar.MONTH,Calendar.DECEMBER);
calendar.set(Calendar.DAY_OF_MONTH,16);
calendar.set(Calendar.HOUR_OF_DAY,11);
calendar.set(Calendar.MINUTE,50);
calendar.set(Calendar.SECOND,0);
//设定时间为 2016年12月16日11点50分0秒
AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(this, AlarmService.class);
intent.setAction(AlarmService.ACTION_ALARM);
PendingIntent pendingIntent = PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
if(Build.VERSION.SDK_INT < 19){
am.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendingIntent);
}else{
am.setExact(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendingIntent);
}
前面介绍的所有的set方法其实都是调用内部的一个private的方法setImpl(),只是不同的set方法传入的值不同而已。
private void setImpl(int type, long triggerAtMillis, long windowMillis, long intervalMillis,
int flags, PendingIntent operation, final OnAlarmListener listener, String listenerTag,
Handler targetHandler, WorkSource workSource, AlarmClockInfo alarmClock) {
if (triggerAtMillis < 0) {
/* NOTYET
if (mAlwaysExact) {
// Fatal error for KLP+ apps to use negative trigger times
throw new IllegalArgumentException("Invalid alarm trigger time "
+ triggerAtMillis);
}
*/
triggerAtMillis = 0;
}
......
try {
mService.set(mPackageName, type, triggerAtMillis, windowMillis, intervalMillis, flags,
operation, recipientWrapper, listenerTag, workSource, alarmClock);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
这里只展示了相关代码,而具体控是否精确是靠windowMillis这个参数
再看看普通的set()与setRepeating()方法如何传递windowMillis参数
public void set(int type, long triggerAtMillis, PendingIntent operation) {
setImpl(type, triggerAtMillis, legacyExactLength(), 0, 0, operation, null, null,
null, null, null);
}
public void setRepeating(int type, long triggerAtMillis,
long intervalMillis, PendingIntent operation) {
setImpl(type, triggerAtMillis, legacyExactLength(), intervalMillis, 0, operation,
null, null, null, null, null);
}
可以发现windowMillis参数为legacyExactLength()方法返回值的,那么我们接着在看legacyExactLength方法
可以看出mAlwaysExact这个变量控制着该方法的返回值,如果是小于API19的版本会使用
WINDOW_EXACT参数,这个参数是0(意思就是区间设置为0,那么就会按照triggerAtMillis这个时间准时触发,也就是精准触发)另一个参数WINDOW_HEURISTIC的值是-1,这个值具体的用法就要看AlarmManagerService具体的实现了,反正只要知道这个值是不精准就可以。而setExact()这个值为WINDOW_EXACT,setWindow()的话这个值你可以自己传所以19以后他们是精准的。
本篇blog只以getService()方式举了栗子,还可通过getBroadCast()发送广播或getActivity()启动Activity来执行某项固定任务。其中各方法的最后一个参数含有以下常量分别代表不同含义的任务执行效果:
FLAG_CANCEL_CURRENT:如果当前系统中已经存在一个相同的PendingIntent对象,那么就将先将已有的PendingIntent取消,然后重新生成一个PendingIntent对象。
FLAG_NO_CREATE:如果当前系统中不存在相同的PendingIntent对象,系统将不会创建该PendingIntent对象而是直接返回null。
FLAG_ONE_SHOT:该PendingIntent只作用一次。在该PendingIntent对象通过send()方法触发过后,PendingIntent将自动调用cancel()进行销毁,那么如果你再调用send()方法的话,系统将会返回一个SendIntentException。
FLAG_UPDATE_CURRENT:如果系统中有一个和你描述的PendingIntent对等的PendingInent,那么系统将使用该PendingIntent对象,但是会使用新的Intent来更新之前PendingIntent中的Intent对象数据,例如更新Intent中的Extras。
AlarmManager非常适合Android中定时任务.并且因为他具有唤醒CPU的功能,可以保证每次需要执行特定任务时CPU都能正常工作,
或者说当CPU处于休眠时注册的闹钟会被保留(可以唤醒CPU)。