请选择 进入手机版 | 继续访问电脑版

Android AlarmManger 精准定时

[复制链接]
为你演绎 发表于 2021-1-1 18:29:25 | 显示全部楼层 |阅读模式 打印 上一主题 下一主题
  业精于勤荒于嬉,写文章训练表达本领,写代码训练基本工。
Android 背景精准定时任务

  引子

在Android平台上,要实现一个背景任务定时执行,并不是什么难事,方案有许多:

  • 使用handler的sendMessageDelayed或sendMessageAtTime方法。
  • 使用Timer + TimerTask。
  • 使用AlarmManger。
  • 使用WorkManager(周期性任务隔断大于15分钟以上)
不外,方式1,2都依赖于实现的线程生命周期,应用历程一旦停止,定时任务无法执行,而且如果CPU处在休眠状态,这两种方式是无法唤醒的,更不消说android在6.0之后为了延长电池寿命增加对背景任务的一系列限制措施,点击参考官网《针对低电耗模式和应用待机模式举行优化》,定时任务无法包管精准执行。
总之,如果你的背景任务,需要在历程退出后也能定时执行,而且具备唤醒CPU的本领,使用AlarmManger是不二选择
点击参考官网《背景处置惩罚指南》
  方案

AlarmManger+Service+BroadcastReceiver
  问题1:设置一次性精准定时任务的API兼容性

构造一个Intent,告诉闹铃何时启动。
  1.                 Calendar calendar_now = Calendar.getInstance();        Calendar calendar_target = Calendar.getInstance();        calendar_target.set(Calendar.HOUR_OF_DAY,hour);        calendar_target.set(Calendar.MINUTE,minute);        calendar_target.set(Calendar.SECOND,0);        calendar_target.set(Calendar.MILLISECOND,0);        //如果设置的时间已颠末去了,推迟到第二天的这个时刻。        if(calendar_target.before(calendar_now)){            calendar_target.add(Calendar.DATE,1);        }        // 注册AlarmManager的定时服务        Intent intent=new Intent(context, BackUpAlarmService.class);        //获取闹钟服务        AlarmManager am = (AlarmManager)context.getSystemService(ALARM_SERVICE);        //8.0,必须启动前台服务,注意需要申请 android.permission.FOREGROUND_SERVICE权限        PendingIntent pendingIntent;        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){            pendingIntent = PendingIntent.getForegroundService(context, REQUEST_CODE,                    intent, PendingIntent.FLAG_UPDATE_CURRENT);        }else{            pendingIntent = PendingIntent.getService(context, REQUEST_CODE,                    intent, PendingIntent.FLAG_UPDATE_CURRENT);        }
复制代码
设置闹铃
  1.                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {            //纵然设备处于低电耗模式也会触发的闹钟。             alarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime(), pendingIntent);        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {            alarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime(), pendingIntent);        } else {            alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime(),  pendingIntent);        }
复制代码
问题2:设置重复精准定时任务

  1. //此方法把设备关闭屏幕,不接入电源,测试照旧会有延迟,但误差都在秒级别//intervalMillis不能在1分钟以内,纵然设置会被系统强制修改成1分钟。Value will be forced up to 60000 as of Android 5.1; don't rely on this to be exactam.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, calendar_target.getTimeInMillis(),intervalMillis, pendingIntent);
复制代码
如果仍要求准确执行,可以接纳设置一次性定时任务,在PendingIntent触发闹铃后,再设置一次即可。
  1. public class BackUpAlarmService extends Service {    private static final String NOTIFICATION_CHANNEL = "com.alarm.service.notification";    private static final int ID = 0x8849;    private static final String TAG = "BackUpAlarmService";    private final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());    public BackUpAlarmService() {    }    @Override    public IBinder onBind(Intent intent) {        return null;    }    @Override    public void onCreate() {        super.onCreate();        android.util.Log.d(TAG,"onCreate");    }    @Override    public int onStartCommand(Intent intent, int flags, int startId) {        android.util.Log.d(TAG,"onStartCommand : startId = "+startId);        android.util.Log.d(TAG,"time : "+dateFormat.format(new Date()));        createNotification();       //do something       //在这里,再次设置定时任务,即可实现精准重复定时任务。        return START_STICKY;    }        //5秒内调用startForeground,防止异常 :  Context.startForegroundService() did not then call Service.startForeground()     private void createNotification(){        if(mBuilder==null){            NotificationManager notificationManager = (NotificationManager) getSystemService                    (NOTIFICATION_SERVICE);            mBuilder = new NotificationCompat.Builder(this,NOTIFICATION_CHANNEL);            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {                NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL, "dc back up", NotificationManager.IMPORTANCE_DEFAULT);                mBuilder.setChannelId(NOTIFICATION_CHANNEL);                notificationManager.createNotificationChannel(channel);            }            mBuilder.setContentText("back up");            mBuilder.setContentTitle("running");            mBuilder.setOnlyAlertOnce(true);            mBuilder.build().flags |= Notification.FLAG_ONGOING_EVENT;            mBuilder.build().flags |= Notification.FLAG_NO_CLEAR;        }        startForeground(ID, mBuilder.build());    }}
复制代码
问题3:取消定时任务

  1.     public static void cancel(Context context) {        // 取消AlarmManager的定时服务        Intent intent=new Intent(context, BackUpAlarmService.class);// 和设定闹钟时的action要一样        //获取闹钟服务        AlarmManager am = (AlarmManager)context.getSystemService(ALARM_SERVICE);        // 这里PendingIntent的requestCode、intent和flag要和设定闹钟时一样        //8.0        PendingIntent pendingIntent;        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){            pendingIntent = PendingIntent.getForegroundService(context, REQUEST_CODE,                    intent, PendingIntent.FLAG_UPDATE_CURRENT);        }else{            pendingIntent = PendingIntent.getService(context, REQUEST_CODE,                    intent, PendingIntent.FLAG_UPDATE_CURRENT);        }        am.cancel(pendingIntent);        context.stopService(intent);    }
复制代码
问题4:设备重启仍然生效

点击参考,《在设备重启时启动闹钟》
  1. //注意在清单文件申请权限:     public class BootBroadcastReceiver extends BroadcastReceiver {    @Override    public void onReceive(Context context, Intent intent) {                if(intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)){            //设置定时任务,可以把时间参数长期化生存,在这里读取后设置。        }            }}
复制代码
问题5:不想Doze和StandBy模式影响自己的定时任务

方案1:调用 setAndAllowWhileIdle()setExactAndAllowWhileIdle()

方案2:应用申请电池优化白名单

只管不这样,除非低电耗模式或应用待机模式破坏了应用的核心功能
  1. //注意申请权限://public void ignoreBatteryOptimization(Activity activity) {     if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){            PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);                    boolean hasIgnored = powerManager.isIgnoringBatteryOptimizations(activity.getPackageName());                    //  判断当前APP是否有加入电池优化的白名单                    if(!hasIgnored) {                          Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);                          intent.setData(Uri.parse("package:"+activity.getPackageName()));                          startActivity(intent);            }    }}
复制代码
最后想说的

本文论述了如何实现一个精准的,可重复定时任务,有时候是因为业务需求硬性要求。如果你仔细检察android官网,会得到以下结论:


  • 应只管制止使用准确的闹钟,除非你的需求有严格时间要求。
  • 尽大概低落闹钟的触发频率,制止应用都在同一时间访问服务器,造成服务器繁忙(如果有的话,可以调用非精准闹铃方法)。
  • 为了延长设备的电池续航时间,为你的用户着想。
  参考

官网《背景处置惩罚指南》
官网 《设置重复闹铃时间》
官网 《针对低电耗模式和应用待机模式举行优化》
官网 《背景优化》
关于使用AlarmManager的注意事项
AlarmManager计时不准,在手机灭屏后延迟的问题
Android API 19 及以上版本AlarmManager setRepeating 不准或只执行一次的办理方案
AlarmManager实现精准定时任务
AlarmManager-系统推荐的定时任务

来源:https://blog.csdn.net/lucky_tom/article/details/111573918
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则


专注素材教程免费分享
全国免费热线电话

18768367769

周一至周日9:00-23:00

反馈建议

27428564@qq.com 在线QQ咨询

扫描二维码关注我们

Powered by Discuz! X3.4© 2001-2013 Comsenz Inc.( 蜀ICP备2021001884号-1 )