关于此功能之前的文章有过大概介绍,现在来阐述自己遇到的一些坑:
上次说道Android:关于定时任务重启之后的问题研究
里面的方案会产生一些问题,设置定时或者取消定时,我是独立放在一个类似于工具类里,重启接收到开机广播之后会进行一次设置定时,主要是为了解决alarmmanager重启后会使得定时失效(查看到定时队列里的定时会被清除)的问题。
但是会产生一个重复定时的问题,对于定时切换一个用户,先定一个时间1,手动切换后,再重新设置过一个定时2,这时候查看定时队列发现,会有两个定时时间,但是实际需要的是一个更新的定时时间2,即发现,定时1并没有被cancel。
我初始化的地方:都是写在LifeTimingFormatter.java里
if(pendingIntentStart == null){
intentStart = new Intent(context, LifeDomainAlarmService.class);
intentStart.setAction(LifeDomainAlarmService.ACTION_ALARM_START);
pendingIntentStart = PendingIntent.getService(context, 0, intentStart, PendingIntent.FLAG_IMMUTABLE|PendingIntent.FLAG_UPDATE_CURRENT);
}
if(pendingIntentEnd == null){
intentEnd = new Intent(context, LifeDomainAlarmService.class);
intentEnd.setAction(LifeDomainAlarmService.ACTION_ALARM_END);
pendingIntentEnd = PendingIntent.getService(context, 1, intentEnd, PendingIntent.FLAG_IMMUTABLE|PendingIntent.FLAG_UPDATE_CURRENT);
}
设置定时的代码:
//Add alarm task to switch domains.
public void setAlarm(Context context, AlarmManager am, int type){
cancelAlarm(context,am);
int userid = getIsLifeDomain(context);
Log.d(TAG," setAlarm userid : "+userid+";");
if(userid == 0){
am.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP,getTime(context,0), pendingIntentStart);
Log.d(TAG," setAlarm pendingIntentStart : "+pendingIntentStart.toString()+";");
}else{
am.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP,getTime(context,1), pendingIntentEnd);
Log.d(TAG," setAlarm pendingIntentEnd : "+pendingIntentEnd.toString()+";");
}
}
取消定时:
public void cancelAlarm(Context context, AlarmManager am){
if(pendingIntentStart != null){
am.cancel(pendingIntentEnd);
}
if(pendingIntentEnd != null){
am.cancel(pendingIntentEnd);
}
}
看起来能正常的定时正常的取消,其实存在一个问题,LifeTimingFormatter.java会在不同的地方被实例化,实例化的同时会将对pendingIntentStart和pendingIntentEnd重复实例化,切换用户之后,再次设置时间前希望能cancel前一个定时,却拿不到前一个用户的pendingIntentStart和pendingIntentEnd来cancel,这个问题把我难住了。(其实我一直想做的是使用单例实现的,但…)。
其实我之前整个定时功能的大致是参照着Android系统的护眼模式,我之前处于一些考虑,我觉得自己的功能比它少些,我就简化整个定时功能了,现在回过头去看这些问题,我发现,护眼模式或者深色主题的定时,都有各自的manager和services进行管理,services会在开机的时候启动,这样不会存在无法cancel,并且也有对系统时间改变的广播监听(因为我这边暴露出来的另一个问题就是,当系统时间改变的时候,我没有对这个定时时间进行一个重新设置,导致还按照之前定时的时间周期来执行)。
先前为了实现每日定时的功能,我采用的方案是使用setRepeating方法,它里面有个参数可以设置一定的时间周期来重复网上有些说法不太一样,有的说是会很精确,但又有说方法过时,不够精确的,我实践结果就是确实是不够精确的,所以为了精确,我更换了一个方法setExactAndAllowWhileIdle,这个是一次性定时,所以我会在定时结束之后再一次设置定时,达到每日定时的效果。
更换方法之后也为了之后解决重复定时问题做了些铺垫,我会在定时响应前做出判断,更新的定时时间我会存储起来,也就是定时队列里重复定时的那个,不会被存储,我会拿存储的定时时间和系统时间作出比较,如果alarmmanager响应定时时,我发现我存储的定时时间和此时的系统时间不匹配,那我就会不响应此次定时,那此次定时过后,这个定时也就过了,也不会重复存在一个不正确的定时提醒。只能说这个方法对定时方法的精确性依赖很高,如果此方法精确不高,那就会出现在该定时响应的时候,被我的判断条件拦截到。