背景:项目中需要给设备实现一个每天自动开关机的功能,然后设备的api是必须要传入具体的“年月日时分”,但是后台推送到设备的参数只有“时分秒”(后面不讨论秒的事),即客户端需要根据时和分,通过一定的算法,来实现每天自动开关机的逻辑。

当然,这个问题并不算难,最多是有点绕,但是我觉得也是可以作为一个问题来讨论的。感兴趣的小伙伴也可以先自己思考一下这个问题再接着往下看。

我们遇到的所有问题的解决,都是从理清楚思路开始的

首先要明确几个前提条件

1.设备一旦关机,将无法通过代码的方式来唤醒开机,除非是一开始定义关机的时候,也把开机的时间设置下去;

2.接收到设置开关机的推送只有一次,且只有时间,没有日期,且在无人工干预的情况下,做到每天自动开关机,其实需要考虑的不是“这一次”开关机的日期和时间,而是“下一次”;

3.开机时间和关机时间的先后关系,以及当前时间与二者的先后关系,都会影响最终的日期的结果

可能说到这里,还有点绕,那么不如尽可能的把所有条件都推演一下,也许答案就在其中

Android原生态开关机动画 原生安卓自动开关机_json

 这张图,就是推演的结果,感兴趣的小伙伴,也可以按照图中的推演顺序,走一遍,看看是否能够理解这个过程。

其实图中所有的时间都是“开区间”,并没有列出闭区间的情况,也就是比如说刚好设置了11点关机或开机的情况,其实很简单,开机的动作讨论闭区间是没有意义的,因为如果现在是开机,那么接收到开机指令是不会处理的,更不会考虑那一分钟的问题,但是对于关机,设置了11点0分,而现在刚好就是11点0分,要不要关机?

从图中也可以知道,

如果说要关机,那么就会走②或者⑥的情况;

如果说不关机,那么也就是走①或者⑤的情况

所以,开区间关机还是闭区间关机,根据实际的业务需求来选择即可。

能够看出来,推演所有场景的目的,就是为了得出具体的开关机的年月日,因为时分秒我们是知道的,所以最终我们可以这么来抽象这个问题

Android原生态开关机动画 原生安卓自动开关机_json_02

 单纯的分析这个问题肯定是不足够的,毕竟我在标题里面写了(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();

这样的话,就完成了通过后台返回“时-分”来实现每天自动开关机的功能了