Android定时任务

本章目录

  • Part One:Timer
  • Part Two:AlarmManager

Android中有很多种实现定时任务的方式,比如Timer,CountDownTimer, AlarmManager,handler和Thread。不过,主要常用的有三种:

  1. Timer(Java遗留的)
  2. Handler(下雪动画那篇使用过了)
  3. AlarmManager(Android官方推荐)

Part One:Timer

Timer是一个定时器工具,包含一系列的schedule方法用于实施定时计划。TimerTask是一个子线程的抽象类,方便在后台处理一些比较复杂的逻辑,然后利用Handler在主线程刷新UI。
下面我们通过修改先前的案例来认识一下这两个类的具体应用。

  • 首先,在activity_main.xml的cacheContainer_main布局容器中,放一个TextView,用来显示滚动文字。
        
  • 然后在MainActivity里面初始化这个TextView和SnowView(这里初始化SnowView是用来关闭自定义View的绘制)。
    private TextView textView;
    private SnowView snowView;
    
    private void initViews() {
        cacheContainer = findViewById(R.id.cacheContainer_main);
        animContainer = findViewById(R.id.animContainer_main);
        leftAnimImageView = findViewById(R.id.leftAnimContainer_main);
        rightAnimImageView = findViewById(R.id.rightAnimContainer_main);
        textView = findViewById(R.id.textView_main);
        snowView = findViewById(R.id.snowView_main);
    }
  • 接着初始化一段文字数组,用来以滚动的方式,不断更新显示的文字。
    private String[] contents = new String[]{"时间久了,我都快忘记我们相识多少年了,",
            "我只知道我认识你很久了,", "漫长的时间却拉不近我们的距离,",
            "一天一点爱恋,一夜一点思念,", "不想再等了,有些话想对你说。"};
  • 最后,把先前定义的openWindow方法改成switchContent,让方法名字表达的更准确一些。同时在方法里初始化Timer和TimerTask,并调用相应的方法。
    private int index = 0;//数组的索引,用于让TextView显示不同内容,初识从0开始
    private void switchContent() {
        final Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                //刷新UI必须执行在主线程
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        //如果字符串数组全部显示完毕,下标清0,防止越界。同是,让自定义View停止绘制。
                        //Timer停止计划任务,并执行开窗动画
                        if (index == contents.length){
                            index = 0;
                            snowView.stopDraw();
                            initCache();
                            timer.cancel();
                        }
                        //让TextView滚动显示文字
                        textView.setText(contents[index]);
                        index++;
                    }
                });
            }
        }, 50, 4000);
    }

整个计划任务的实现逻辑并不复杂,就是不断变换数组索引坐标,让TextView显示不同的内容。
这里要重点说一下我们使用的timer.schedule(task, delay, period)的三个参数:

  1. task:TimerTask对象,就是要周期执行的任务。
  2. delay:从计时器初始化完毕后,开始启动的延迟时间。
  3. period:定时器的间隔时间。
    比如,我们的案例就是50毫秒后启动定时器,没4秒更新一下文字,文字刷新完执行动画。


    滚动文字.gif

Part Two:AlarmManager

前面提到的Timer实现定时任务的方式使用的是Java的API,大部分情况下都是满足需求的,比如本例。但是如果遇到手机锁屏或者关机,唤醒CPU执行定时任务时,就会遇到一些问题,比如闹钟。所以,官方推荐使用AlrmManager来执行定时任务。
AlarmManager的实现方式也不是很复杂,只是需要用到PendingIntent,具体步骤如下:

  • 先前的准备工作不变,只是改变switchContent方法。首先,注释掉先前写的代码,然后声明一个全局的AlarmManager
    private void switchContent() {
        alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
    }

    private AlarmManager alarmManager;
  • 然后声明一个全局的PendingIntent,并设置AlarmManager的重复方式
    private void switchContent() {
        alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
        Intent intent = new Intent();
        intent.setAction("action.REFRESHTEXTVIEW");
        pendingIntent = PendingIntent.getBroadcast(this,
                100, intent, PendingIntent.FLAG_CANCEL_CURRENT);
        alarmManager.setRepeating(AlarmManager.RTC_WAKEUP,
                5000, 60000, pendingIntent);
    }

    private AlarmManager alarmManager;
    private PendingIntent pendingIntent;

其中,PendingIntent是个延迟意图对象,这里是发送广播,所以使用getBroadcast方法,也可以使用getService启动Service或者getActivity启动Activity来执行某项固定任务。getBroadcast的参数前面都好理解,最后一个参数的效果有:

  1. FLAG_CANCEL_CURRENT:如果系统中有一个相同的PendingIntent对象,那么就取消旧的,然后重新生成一个。
  2. FLAG_NO_CREATE:如果当前系统中不存在相同的PendingIntent对象,系统将不会创建该PendingIntent对象而是直接返回null。
  3. FLAG_ONE_SHOT:该PendingIntent只作用一次。在该PendingIntent对象通过send()方法触发过后,PendingIntent将自动调用cancel()进行销毁,那么如果你再调用send()方法的话,系统将会返回一个SendIntentException。
  4. FLAG_UPDATE_CURRENT:如果系统中有一个和你描述的PendingIntent对等的PendingInent,那么系统将使用该PendingIntent对象,但是会使用新的Intent来更新之前PendingIntent中的Intent对象数据,例如更新Intent中的Extras。
    另外,setRepeating也需要注意,从Android5.0开始,第二个参数triggerAtMillis(触发时间)不得低于5秒,intervalMillis(重复间隔)不得低于60秒。这两个参数如果比这两个值小,默认分别是5秒和60秒,只有大于才会生效,因为间隔太短会费电~短时间的定时任务,官方推荐使用Handler方式。
    所以,其实AlarmManager并不适合本例,这里只是讲一下用法。
    setRepeating方法的第一个参数也单独说下:
  5. AlarmManager.RTC_WAKEUP表示闹钟在睡眠状态下会唤醒系统并执行提示功能,该状态下闹钟使用绝对时间,状态值为0;
  6. AlarmManager.RTC表示闹钟在睡眠状态下不可用,该状态下闹钟使用绝对时间,即当前系统时间,状态值为1;
  7. AlarmManager.ELAPSED_REALTIME_WAKEUP表示闹钟在睡眠状态下会唤醒系统并执行提示功能,该状态下闹钟也使用相对时间,状态值为2;
  8. AlarmManager.ELAPSED_REALTIME表示闹钟在手机睡眠状态下不可用,该状态下闹钟使用相对时间(相对于系统启动开始),状态值为3;
  9. AlarmManager.POWER_OFF_WAKEUP表示闹钟在手机关机状态下也能正常进行提示功能,所以是5个状态中用的最多的状态之一,该状态下闹钟也是用绝对时间,状态值为4;不过本状态好像受SDK版本影响,某些版本并不支持;
  • 接下来就是自定义一个广播接收者,当接收到广播时,执行的任务:
    private class AlarmBroadcastReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (index == contents.length) {
                index = 0;
                snowView.stopDraw();
                initCache();
                alarmManager.cancel(pendingIntent);//取消定时器
            }
            //让TextView滚动显示文字
            textView.setText(contents[index]);
            index++;
        }
    }
  • 最后,将广播注册即可
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction("action.REFRESHTEXTVIEW");
        BroadcastReceiver receiver = new AlarmBroadcastReceiver();
        registerReceiver(receiver, intentFilter);

结果就是每隔60秒发送一个广播,广播接收器收到广播后,执行相应的任务。

你可能感兴趣的:(Android定时任务)