Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

使用高精度定时装置来完成App的日间模式&夜间模式切换 #74

Open
soapgu opened this issue Aug 28, 2021 · 0 comments
Open
Labels
Demo Demo 安卓 安卓

Comments

@soapgu
Copy link
Owner

soapgu commented Aug 28, 2021

  • 需求

就是App应用需要在7:00~19:00直接把皮肤设为浅色系,在19:00到第二天的7:00皮肤设为深色系

  • 换肤实现

Android里面的resouce体系里面有config_qualifier,
其中就有夜间模式,需要在资源管理器中配置好
图片

  • 准备好资源,那资源怎么完成转换那?
    public static void setDefaultNightMode (int mode)
    这个API可以完成切换,让我再看看参数
  1. public static final int MODE_NIGHT_AUTO
    看上去是最合适的参数,自动切多好,但是啊但是
    This constant is deprecated.
    Use MODE_NIGHT_AUTO_TIME instead ,转方向。。。

  2. public static final int MODE_NIGHT_AUTO_TIME
    This constant is deprecated.
    Automatic switching of dark/light based on the current time is deprecated. Considering using an explicit setting, or MODE_NIGHT_AUTO_BATTERY.
    一样不能用了,只能设成和电源相关了

  3. public static final int MODE_NIGHT_YES
    Night mode which uses always uses a light mode, enabling notnight qualified resources regardless of the time.

  4. public static final int MODE_NIGHT_NO
    Night mode which uses always uses a dark mode, enabling night qualified resources regardless of the time.

看来后面两次参数没啥问题,问题就是必须自己下场动手定时设置了

  • 寻找合适的定时装置

首先第一反应是Timer这个类型。可以说这个类型是个经典了。定期世界能触发线程执行相关代码逻辑。用这个类来实现一定可以完成需求,但是总是觉得有点不爽。

  • 我的实际需求是一天只切换两次
  • 我的时间间隔怎么设置都是尴尬
  • 设高了一小时一次,那我就不能精确的切换皮肤了
  • 设低了一秒一次,感觉又白白浪费资源,虽然每次执行占用cpu都很小,但是频繁的线程切换也一样是浪费资料
  • 还有一个致命问题,就是安卓如果进入深度睡眠,那我的timer根本就不会执行,对就是睡死了
    图片

其实我需要的就是这个家伙
图片
对就是这种定时器,当然炸弹我们不要~~~~

安卓手机里面不是有闹钟嘛,感觉和我的需求有共通点。我们可以内置一个自定义的闹钟吗?
感谢Android系统,他提供了我在windows桌面时代没法实现的精确定时执行的功能,接下来由我娓娓道来。

  • AlarmManager

This class provides access to the system alarm services. These allow you to schedule your application to be run at some point in the future.
强大的类,和系统服务alarm services相关。可以让我们放一定自定义的计划进去,待遇和系统里的闹钟一样了

  • PendingIntent

A description of an Intent and target action to perform with it. Instances of this class are created with getActivity, getActivities, getBroadcast, and getService;
顾名思义,Intent我们已经很熟悉了,PendingIntent就是延时的Intent。
定时执行的内容当然也是严格限制的,不是为所欲为,你也不能真放一个炸弹进去了
图片
一共有四种类型

  1. getActivity
    执行Activity的Intent
  2. getActivities
    批量执行Activity的Intent
  3. getBroadcast
    执行Broadcast的Intent
  4. getService;
    执行Service相关的Intent
  • 代码实现部分

App初始化阶段先发送一个Broadcast的Intent

        Intent intent = new Intent();
        intent.setAction(Intents.SetThemeAction);
        context.sendBroadcast(intent);

创建SetThemeReceiver类作为广播接收器

public class SetThemeReceiver extends BroadcastReceiver {

    private static final int REQUEST_CODE  = 805;
    private static final int hour = 7;

    /**
     * 定时下次启动切换主题
     * @param context 上下文
     * @param year 当前年
     * @param month 当前月
     * @param day 当前日
     * @param currentHour 当前小时
     */
    private void setNextChange(Context context,int year, int month , int day , int currentHour){
        Calendar next = Calendar.getInstance();
        int nextHour = hour;
        boolean nextDay = false;

        if( currentHour >= hour && currentHour < hour + 12 ){
            nextHour += 12;
        }
        else if( currentHour >= hour + 12 ){
            nextDay = true;
        }
        next.set( year, month, day, nextHour, 0 , 0);
        if( nextDay ) {
            next.add(Calendar.DAY_OF_MONTH, 1);
        }

        Logger.i("Next invoke time:%s" ,SimpleDateFormat.getDateTimeInstance().format( next.getTime() ));

        AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
        Intent intent = new Intent(Intents.SetThemeAction);
        PendingIntent pendingIntent = PendingIntent.getBroadcast(context,
                REQUEST_CODE, intent,
                PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE);
        alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP,
                next.getTimeInMillis() , pendingIntent);
    }

    /**
     * 获取当前night mode
     * @return MODE_NIGHT
     */
    private int getNightMode( int currentHour ){

        if( currentHour >= hour &&currentHour < hour + 12 ){
            return AppCompatDelegate.MODE_NIGHT_NO;
        }
        return AppCompatDelegate.MODE_NIGHT_YES;
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.getAction().equals(Intents.SetThemeAction)) {
            Calendar calendar = Calendar.getInstance();
            int currentHour = calendar.get(Calendar.HOUR_OF_DAY);

            int nightMode = this.getNightMode(currentHour);
            String mode = nightMode == AppCompatDelegate.MODE_NIGHT_NO ? "MODE_NIGHT_NO":"MODE_NIGHT_YES";
            Logger.i( "-----current night mode:%s-----", mode );
            AppCompatDelegate.setDefaultNightMode(nightMode);
            setNextChange(context, calendar.get(Calendar.YEAR),calendar.get(Calendar.MONTH),calendar.get(Calendar.DAY_OF_MONTH), currentHour);
        }
    }
}
  • 主要逻辑
  1. onReceive
    先接受到广播后,先设置下当前的nightMode
    再调用setNextChange来创建广播定时器

  2. setNextChange
    计算出下一次切换皮肤的时间点,创建相关PendingIntent,通过alarmManager放入系统alarm service里面

  3. 系统执行PendingIntent再次触发SetThemeReceiver 的onReceive,进入下一个轮回,生生不息

  • 注意点
  1. PendingIntent.getBroadcast的requestCode,一定要防止冲突,因为和PendingIntent.FLAG_CANCEL_CURRENT 这个flag配套了,可能弄不好把别人的定时任务冲掉了就不好了

  2. AlarmType
    AlarmManager.RTC:硬件时间,不唤醒休眠设备;当休眠时不发起闹钟。
    AlarmManager.RTC_WAKEUP:硬件时间,当闹钟发射时唤醒休眠设备;
    AlarmManager.ELAPSED_REALTIME:真实时间流逝,不唤醒休眠设备;当设备休眠时不发起闹钟。
    AlarmManager.ELAPSED_REALTIME_WAKEUP:真实时间流逝,当闹钟发起时唤醒手机休眠;

    RTC闹钟和ELAPSED_REALTIME 最大的差别就是前者可以通过修改手机时间触发闹钟事件,
    后者要通过真实时间的流逝,即使在休眠状态,时间也会被计算。

  3. 精确的闹钟的代价
    精确的闹钟被不被API推荐
    如需获取这种特殊应用访问权限,请在清单中请求 SCHEDULE_EXACT_ALARM 权限

参考链接

@soapgu soapgu added Demo Demo 安卓 安卓 labels Aug 28, 2021
@soapgu soapgu changed the title 使用高精度定时装置来完成App的日间模式&日间模式切换 使用高精度定时装置来完成App的日间模式&夜间模式切换 Aug 28, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Demo Demo 安卓 安卓
Projects
None yet
Development

No branches or pull requests

1 participant