// 创建意图,启动MonthlyTaskService
Intent 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() {
}
@Override
public 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;
}
@Override
public 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以上 启动服务时,要启动前台通知。告知用户。否则会崩溃
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 。经测试,就算杀掉进程,任务也可以执行。
闹钟触发方式
// 注意:在实际应用中,你可能需要根据实际需求选择合适的触发方式,
// 例如使用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:任务执行内容
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");
}
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
关于Android中设置闹钟的相对完善的解决方案