背景:项目中需要给设备实现一个每天自动开关机的功能,然后设备的api是必须要传入具体的“年月日时分”,但是后台推送到设备的参数只有“时分秒”(后面不讨论秒的事),即客户端需要根据时和分,通过一定的算法,来实现每天自动开关机的逻辑。
当然,这个问题并不算难,最多是有点绕,但是我觉得也是可以作为一个问题来讨论的。感兴趣的小伙伴也可以先自己思考一下这个问题再接着往下看。
我们遇到的所有问题的解决,都是从理清楚思路开始的
首先要明确几个前提条件
1.设备一旦关机,将无法通过代码的方式来唤醒开机,除非是一开始定义关机的时候,也把开机的时间设置下去;
2.接收到设置开关机的推送只有一次,且只有时间,没有日期,且在无人工干预的情况下,做到每天自动开关机,其实需要考虑的不是“这一次”开关机的日期和时间,而是“下一次”;
3.开机时间和关机时间的先后关系,以及当前时间与二者的先后关系,都会影响最终的日期的结果
可能说到这里,还有点绕,那么不如尽可能的把所有条件都推演一下,也许答案就在其中
这张图,就是推演的结果,感兴趣的小伙伴,也可以按照图中的推演顺序,走一遍,看看是否能够理解这个过程。
其实图中所有的时间都是“开区间”,并没有列出闭区间的情况,也就是比如说刚好设置了11点关机或开机的情况,其实很简单,开机的动作讨论闭区间是没有意义的,因为如果现在是开机,那么接收到开机指令是不会处理的,更不会考虑那一分钟的问题,但是对于关机,设置了11点0分,而现在刚好就是11点0分,要不要关机?
从图中也可以知道,
如果说要关机,那么就会走②或者⑥的情况;
如果说不关机,那么也就是走①或者⑤的情况
所以,开区间关机还是闭区间关机,根据实际的业务需求来选择即可。
能够看出来,推演所有场景的目的,就是为了得出具体的开关机的年月日,因为时分秒我们是知道的,所以最终我们可以这么来抽象这个问题
单纯的分析这个问题肯定是不足够的,毕竟我在标题里面写了(Android),当然Java也是一样的,接下来分享一下最终抽象出来的工具类
public class RebootUtil {
//定义SP值,用来缓存开机时间和关机时间
public static final String POWER_ON_TIME = "power_on_time";
public static final String POWER_OFF_TIME = "power_off_time";
//定义两个关机开机数组,用于调用接口
private static int[] timeoff = new int[]{0, 0, 0, 0, 0};
private static int[] timeon = new int[]{0, 0, 0, 0, 0};
public static void setReboot() {
//获取设置的开关机时间参数
String on_time = SharedUtil.getString(MyApp.getApplication(), POWER_ON_TIME);
String off_time = SharedUtil.getString(MyApp.getApplication(), POWER_OFF_TIME);
if (TextUtils.isEmpty(on_time) || TextUtils.isEmpty(off_time)) {
LogUtil.e("开机或关机时间为空,return");
return;
}
//一般来说,strOn(strOff).length = 3,这里只取0和1,因为接口只需要时、分,不需要秒
String[] strOn = on_time.split(":");
if (strOn.length >= 2) {
//开机 Hour
timeon[3] = Integer.parseInt(strOn[0]);
//关机 Hour
timeon[4] = Integer.parseInt(strOn[1]);
}
String[] strOff = off_time.split(":");
if (strOff.length >= 2) {
//关机 Hour
timeoff[3] = Integer.parseInt(strOff[0]);
//关机 Minute
timeoff[4] = Integer.parseInt(strOff[1]);
}
//获取日期
String todayDate = getAfterDate(0);
String tomorowDate = getAfterDate(1);
String theDayAfterTomorowDate = getAfterDate(2);
String[] stringToday = todayDate.split("-");
String[] stringTmr = tomorowDate.split("-");
String[] stringafterTmr = theDayAfterTomorowDate.split("-");
//1.比较日期
if (stringToTime(on_time).before(stringToTime(off_time))) {
//如果开机时间的值比关机时间的值小(比如说8小于9)
//此时,开机时间一定是第二天的日期
boolean isOverOnTime = stringToTime(getNowTime()).after(stringToTime(on_time));
boolean isOverOffTime = stringToTime(getNowTime()).after(stringToTime(off_time));
if (!isOverOnTime && !isOverOffTime) {
//如果开机时间和关机时间都没过,关机今天,开机明天
timeoff[0] = Integer.parseInt(stringToday[0]);
timeoff[1] = Integer.parseInt(stringToday[1]);
timeoff[2] = Integer.parseInt(stringToday[2]);
timeon[0] = Integer.parseInt(stringTmr[0]);
timeon[1] = Integer.parseInt(stringTmr[1]);
timeon[2] = Integer.parseInt(stringTmr[2]);
}else{
if(isOverOnTime && isOverOffTime){
//如果开关机时间都过了,关机明天,开机后天
timeoff[0] = Integer.parseInt(stringTmr[0]);
timeoff[1] = Integer.parseInt(stringTmr[1]);
timeoff[2] = Integer.parseInt(stringTmr[2]);
timeon[0] = Integer.parseInt(stringafterTmr[0]);
timeon[1] = Integer.parseInt(stringafterTmr[1]);
timeon[2] = Integer.parseInt(stringafterTmr[2]);
}else{
//这里,按照排列组合,剩下
// 1.开机时间没过,关机时间过了(此种情况下,因为开机时间在前,所以不可能发生该情况)
// 2.开机时间过了,关机时间没过,关机今天,开机明天
timeoff[0] = Integer.parseInt(stringToday[0]);
timeoff[1] = Integer.parseInt(stringToday[1]);
timeoff[2] = Integer.parseInt(stringToday[2]);
timeon[0] = Integer.parseInt(stringTmr[0]);
timeon[1] = Integer.parseInt(stringTmr[1]);
timeon[2] = Integer.parseInt(stringTmr[2]);
}
}
} else {
//比较关机时间是否已经过去了
if ((stringToTime(getNowTime()).after(stringToTime(off_time)))) {
//如果过去了,那么全都是明天
timeon[0] = Integer.parseInt(stringTmr[0]);
timeon[1] = Integer.parseInt(stringTmr[1]);
timeon[2] = Integer.parseInt(stringTmr[2]);
timeoff[0] = Integer.parseInt(stringTmr[0]);
timeoff[1] = Integer.parseInt(stringTmr[1]);
timeoff[2] = Integer.parseInt(stringTmr[2]);
} else {
//如果没过,那就全部是今天
timeon[0] = Integer.parseInt(stringToday[0]);
timeon[1] = Integer.parseInt(stringToday[1]);
timeon[2] = Integer.parseInt(stringToday[2]);
timeoff[0] = Integer.parseInt(stringToday[0]);
timeoff[1] = Integer.parseInt(stringToday[1]);
timeoff[2] = Integer.parseInt(stringToday[2]);
}
}
LogUtil.d("自动开关机:" + "开机时间:" + timeon[0] + "-" + timeon[1] + "-" + timeon[2] + " " + timeon[3] + ":" + timeon[4]);
LogUtil.d("自动开关机:" + "关机时间:" + timeoff[0] + "-" + timeoff[1] + "-" + timeoff[2] + " " + timeoff[3] + ":" + timeoff[4]);
}
/**
* date:表示日期增加的天数,比如
* days=0就是今天的日期;
* days=1就是明天的日期;
* days=2就是后天的日期
*
* @param days
* @return
*/
public static String getAfterDate(int days) {
SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd");
Calendar c = Calendar.getInstance();
c.setTime(new Date());
if (days > 0) {
c.add(Calendar.DAY_OF_MONTH, days);
}
Date date = c.getTime();
return f.format(date);
}
private static String getNowTime() {
SimpleDateFormat f = new SimpleDateFormat("HH:mm:ss");
Calendar c = Calendar.getInstance();
c.setTime(new Date());
Date date = c.getTime();
return f.format(date);
}
}
我这边调用的场景是这样
1.接收到后台的推送,从json中解析出来对应的开关机“时-分”信息
//获取开关机时间
String powerOnTime = jsonObject.getString("powerOnTime");
String powerOffTime = jsonObject.getString("powerOffTime");
SharedUtil.putString(MyApp.getApplication(), POWER_ON_TIME, powerOnTime);
SharedUtil.putString(MyApp.getApplication(), POWER_OFF_TIME, powerOffTime);
//调用方法
RebootUtil.setReboot();
2.每次app启动都再次该方法(重要:开机之后必须再次执行方法,定义下一次的开关机时间)
//重新设置开关机时间
RebootUtil.setReboot();
这样的话,就完成了通过后台返回“时-分”来实现每天自动开关机的功能了