AlarmManager使用
1.创建意图
// 创建意图,启动MonthlyTaskServiceIntent intent = new Intent(getContext(), TimeTaskService.class);
// 传递数据intent.putExtra(TimeTaskService.KEY_TITLE,userRemind.getTitle());intent.putExtra(TimeTaskService.KEY_DEC,userRemind.getDescription());
TimeTaskService.class 是任务执行的具体内容。是一个服务
public class TimeTaskService extends Service {public static final String KEY_TITLE = "key_title";public static final String KEY_DEC = "key_dsc";public TimeTaskService() {}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {// 执行任务逻辑LogUtil.i("shawn", "onStartCommand 任务执行");String string = intent.getStringExtra(KEY_TITLE);String dec = intent.getStringExtra(KEY_DEC);// 任务完成后,停止服务stopSelf();return START_NOT_STICKY;}@Overridepublic IBinder onBind(Intent intent) {return null;}
}
创建通知
private void showNotification(String title, String description) {if (StringUtils.isEmpty(title) || StringUtils.isEmpty(description)) {return;}NotificationManager notificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);// 设置通知点击事件Intent notificationIntent = new Intent(getContext(), MainActivity.class);notificationIntent.putExtra(NotificationConstants.NOTIFICATION_KEY, NotificationConstants.NOTIFICATION_VALUE);PendingIntent pendingIntent = PendingIntent.getActivity(getContext(), 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);Notification.Builder notificationBuilder;// 创建通知渠道if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {NotificationChannel channel = new NotificationChannel(NotificationConstants.NOTIFICATION_CHANNEL_ID, NotificationConstants.NOTIFICATION_CHANNEL_NAME, NotificationManager.IMPORTANCE_DEFAULT);channel.setDescription(NotificationConstants.NOTIFICATION_CHANNEL_DESC);notificationManager.createNotificationChannel(channel);// 构建通知notificationBuilder = (new Notification.Builder(getContext(), NotificationConstants.NOTIFICATION_CHANNEL_ID))
// .setLargeIcon(BitmapFactory.decodeResource(this.getResources(), R.drawable.app_icon)).setSmallIcon(R.drawable.app_icon).setContentTitle(title).setContentText(description).setContentIntent(pendingIntent).setAutoCancel(true);// 设置点击通知后自动取消通知// 启动前台服务startForeground(NotificationConstants.FOREGROUND_ID, notificationBuilder.build());} else {// 构建通知notificationBuilder = new Notification.Builder(getContext())
// .setLargeIcon(BitmapFactory.decodeResource(this.getResources(), R.drawable.app_icon)).setSmallIcon(R.drawable.app_icon).setContentTitle(title).setContentText(description).setContentIntent(pendingIntent).setPriority(Notification.PRIORITY_DEFAULT).setAutoCancel(true);// 设置点击通知后自动取消通知}// 发送通知notificationManager.notify(NotificationConstants.NOTIFICATION_ID, notificationBuilder.build());}
注意:在android 8.0以上 启动服务时,要启动前台通知。告知用户。否则会崩溃
2.创建PendingIntent
PendingIntent pendingIntent;if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {pendingIntent = PendingIntent.getForegroundService(getContext(),(int) requestCode,intent,PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);} else {pendingIntent = PendingIntent.getService(getContext(),(int) requestCode,intent,PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);}
注意:在使用场景中,经过测试发现。如果把进程杀掉,则任务不执行。所以,在上面的代码中,判断当前版本,大于等于 android 26时,使用PendingIntent.getForegroundService 启动前台服务的方式获取pendingIntent 。经测试,就算杀掉进程,任务也可以执行。
3.启动任务
闹钟触发方式
// 注意:在实际应用中,你可能需要根据实际需求选择合适的触发方式,// 例如使用RTC_WAKEUP替代RTC,确保在设备休眠时也能唤醒系统执行任务// 闹钟类型// ELAPSED_REALTIME - 基于自设备启动以来所经过的时间触发待定 intent,但不会唤醒设备。经过的时间包括设备处于休眠状态期间的任何时间。// ELAPSED_REALTIME_WAKEUP - 唤醒设备,并在自设备启动以来特定时间过去之后触发待定 Intent。// RTC - 在指定的时间触发待定 Intent,但不会唤醒设备。// RTC_WAKEUP - 唤醒设备以在指定的时间触发待定 Intent。
执行1次性任务
// 获取AlarmManager实例AlarmManager alarmManager = (AlarmManager) getApplication().getSystemService(Context.ALARM_SERVICE);if (repetitionType == MyConstants.REPEAT_NO) {if (alarmManager != null && pendingIntent != null) {// 设置一次性闹钟LogUtil.i("shawn", "设置一次性闹钟");
// alarmManager.set(
// AlarmManager.RTC_WAKEUP,
// triggerAtMillis,
// pendingIntent
// );
// 设置精确闹钟
// 设置精确重复闹钟//低电耗模式和应用待机模式的影响//为了延长设备的电池续航时间,我们在 Android 6.0(API 级别 23)中引入了低电耗模式和应用待机模式。// 当设备处于低电耗模式时,所有标准闹钟都会推迟,直到设备退出低电耗模式或维护期开始。// 如果必须让某个闹钟在低电耗模式下也能触发,// 可以使用 setAndAllowWhileIdle() 或 setExactAndAllowWhileIdle()。// 您的应用将在处于空闲状态时(即用户在一段时间内未使用应用,并且应用没有前台进程时)进入应用待机模式。// 当应用处于应用待机模式时,闹钟会像设备处于低电耗模式一样被延迟。// 当应用不再处于空闲状态或者当设备接通电源时,该限制便会解除alarmManager.setExact(AlarmManager.RTC_WAKEUP,triggerAtMillis,pendingIntent);}return;}
上面代码有2种任务执行类型,alarmManager.set 设置的不精确闹钟,setExact设置的是精确闹钟,如果对任务的执行时间没有严格的要求,建议使用set方法,对设备的电量友好。
triggerAtMillis:任务执行的时间
设置重复闹钟
if (alarmManager != null && pendingIntent != null) {// 设置重复闹钟alarmManager.setRepeating(AlarmManager.RTC_WAKEUP,triggerAtMillis,intervalMillis,pendingIntent);}
参数解释:
AlarmManager.RTC_WAKEUP:闹钟触发方式
triggerAtMillis:执行时间
intervalMillis:间隔时间
pendingIntent:任务执行内容
4.任务取消
Intent intent = new Intent(getContext(), TimeTaskService.class);intent.setAction("com.calendar.timeTaskService");AlarmManager alarmManager = (AlarmManager) getApplication().getSystemService(Context.ALARM_SERVICE);PendingIntent pendingIntent;if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {pendingIntent = PendingIntent.getForegroundService(getContext(),(int) aLong,intent,PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);LogUtil.i("shawn","getForegroundService job");} else {pendingIntent = PendingIntent.getService(getContext(),(int) aLong,intent,PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);LogUtil.i("shawn","getService job");}if (pendingIntent != null) {LogUtil.i("shawn","cancel job");alarmManager.cancel(pendingIntent);} else {LogUtil.i("shawn","pendingIntent is null");}
5.任务查看
adb shell dumpsys alarm > abc.txt
通过使用adb命令,可以查看当前系统的所有alarm任务,然后筛选包名,查看任务
u0a647:com.calendar.master.gp +11ms running, 1 wakeups:+11ms 1 wakes 1 alarms, last -2m15s264ms:*walarm*:com.calendar.master.gp/.ui.calendar.TimeTaskService
6.参考
关于Android中设置闹钟的相对完善的解决方案