Android 周期任务

AlarmManager使用

1.创建意图

// 创建意图,启动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以上 启动服务时,要启动前台通知。告知用户。否则会崩溃

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中设置闹钟的相对完善的解决方案

你可能感兴趣的:(周期任务,android,java)