欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

android 定时器

程序员文章站 2022-06-09 15:01:43
...

android 定时器 (奇葩需求)

先说说这个定时器滴需求

  • 定时(这个就是废话了)
  • 当页面有定时器时,不管是否是destory了,只要时间没到,当再次进入这个页面时,不能重新计时。比如 A 进入 B 页面 ,在B页面开启定时器(假设60s)后, 回到了A页面 ,10秒后再次进入B ,这时,B页面显示的倒计时为50s(漂亮滴产品小姐姐想出来的需求,汝等屌丝也只能奋力实现啦。。。)。
  • 另外一点,貌似是还需要区分账号,不同滴账号分别计时(只要把上面的实现了,这个就比较好做了,后面会讲到思路)

思路

目前一共有三种实现方式(区别在于计时器的实现方式不同),我实现了前面两种方式,第三种的话,就靠大家自己实现了。而且我也封装了一下,只需要改变实例化的对象,就可以在两种计时方式中随意切换了。

1、全局使用一个计时器,将页面的key与时间保存在一个map中,通过计时器去回调(后面会讲到实例代码)
2、利用一个全局的arraylist,将页面的key与计时器保存在一个map中,在将map放入list里面。(计时器通过实现CountDownTimer来做滴)
3、 将页面的key与开始时间保存在一个map中,然后页面自己持有一个计时器,自己处理。

代码实现

第一种方式

先看接口类(ITimer)

public interface ITimer {

    /**
     * 绑定回调
     * @param name 页面的key
     * @param onNotifyTimeCallback 回调
     */
    void bind(String name, OnNotifyTimeCallback onNotifyTimeCallback);

    /**
     * 任务开始的时候调用
     * 默认情况下返回59~0
     * @param name 页面的key
     * @param onNotifyTimeCallback 回调
     */
    void start(String name, OnNotifyTimeCallback onNotifyTimeCallback);

    /**
     * 任务开始的时候调用
     * 默认情况下返回59~0
     * @param name 页面的key
     * @param onNotifyTimeCallback 回调
     * @param millisInFuture 总时间
     * @param countDownInterval 间隔时间
     */
    void start(String name, OnNotifyTimeCallback onNotifyTimeCallback, long millisInFuture, long countDownInterval);

    /**
     * 移除绑定
     * @param name 页面的key
     */
    void unbind(String name);
}



这个是实现类(TimerHelper )
``java
public class TimerHelper implements ITimer {

    //保存页面时间
    private Map<String, Long> mMap;
    private Timer mTimer;
    private ArrayList<Map<String, OnNotifyTimeCallback>> mCallbacks = new ArrayList<>();

    private static TimerHelper timerHelper;

    //默认时间
    private static final long MILLIS_IN_FUTURE = 60 * 1000;
    private static final long COUNT_DOWN_INTERVAL = 1000;

    private long millisInFuture;
    private long countDownInterval;

    /**
     * 定时timer
     */
    private TimerTask mTimerTask;

    /**
     * TimerHelper 单例
     * @return
     */
    public static TimerHelper getInstance() {
        if (timerHelper == null) {
            synchronized (TimerHelper.class) {
                if (timerHelper == null) {
                    timerHelper = new TimerHelper();
                }
            }
        }
        return timerHelper;
    }

    private TimerHelper() {
        mMap = new HashMap<>();
    }

    @Override
    public void start(String name, OnNotifyTimeCallback onNotifyTimeCallback) {
        start(name, onNotifyTimeCallback, MILLIS_IN_FUTURE, COUNT_DOWN_INTERVAL);
    }

    @Override
    public void start(String name, OnNotifyTimeCallback onNotifyTimeCallback,
                      long millisInFuture, long countDownInterval) {
        this.millisInFuture = millisInFuture;
        this.countDownInterval = countDownInterval;
        putKey(name);
        Map<String, OnNotifyTimeCallback> map = new HashMap<>();
        map.put(name, onNotifyTimeCallback);
        mCallbacks.add(map);
        onNotifyTimeCallback.onNotify(-1L);
    }

    /**
     * 将页面的key与开始时间存入map中,并且启动timer
     * @param name
     */
    private void putKey(String name) {
        if (mMap.isEmpty()) {
            mMap.put(name, System.currentTimeMillis());
            startTimer();
        } else {
            if (!mMap.containsKey(name)) {
                mMap.put(name, System.currentTimeMillis());
            }
        }
    }

    /**
     * 如果map中有key 则返回time,如果没有则加入map中
     * 这个方法会在onresume中或者onstart中调用
     * 目的是为了如果有计时,则重新接收回调
     * 因为回调会在onpause或者onstop中被移除
     * @param name
     * @return 回调-1 表示需要重新开始计时(比如页面第一次打开,或者当前计时完成,需要重新计时)
     */
    @Override
    public void bind(String name, OnNotifyTimeCallback onNotifyTimeCallback) {
        if (mMap.containsKey(name)) {
            long time = millisInFuture - (System.currentTimeMillis() - mMap.get(name));
            if (time <= millisInFuture) {
                Map<String, OnNotifyTimeCallback> map = new HashMap<>();
                map.put(name, onNotifyTimeCallback);
                mCallbacks.add(map);
                onNotifyTimeCallback.onNotify(time / 1000);
            }
        } else {
            onNotifyTimeCallback.onNotify(-1L);
        }
    }


    /**
     * 当页面不在可见时,就不需要接收回调
     * 直接移除
     * @param name 页面的key
     */
    @Override
    public void unbind(String name) {
        for (int i = 0; i < mCallbacks.size(); i++) {
            if (mCallbacks.get(i).containsKey(name)) {
                mCallbacks.remove(i);
                return;
            }
        }
    }

    private void startTimer() {
        mTimer = new Timer();
        mTimerTask = new MyTimeTask();
        mTimer.schedule(mTimerTask, 0, countDownInterval);
    }


    /**
     * 轮训检查
     * 轮训时间按照自己设定的间隔
     * 这里默认是1000ms
     */
    private void check() {
        if (mMap != null && mMap.isEmpty()) {
            mTimerTask.cancel();
            mTimerTask = null;
            mTimer.cancel();
            return;
        }
        sendMessage();
        scheduleMap();
    }

    /**
     * 每次轮训都会给当前页面发送回调
     * 帮助view发生变化
     */
    private void sendMessage() {
        if (mCallbacks.size() != 0) {
            //只会有一个页面有回调
            Map<String, OnNotifyTimeCallback> map = mCallbacks.get(mCallbacks.size() - 1);
            Iterator<Map.Entry<String, OnNotifyTimeCallback>> iterator = map.entrySet().iterator();
            if (iterator.hasNext()) {
                Map.Entry<String, OnNotifyTimeCallback> entry = iterator.next();
                OnNotifyTimeCallback callback = entry.getValue();
                long time = (millisInFuture - (System.currentTimeMillis() - mMap.get(entry.getKey()))) / 1000;
                callback.onNotify(time);
            }
        }
    }

    /**
     * 每次轮训检查超时的任务
     * 然后将超时任务移除
     * 第一步将当前key的map对象移除
     * 第二步将当前key的回调移除
     */
    private void scheduleMap() {
        long currentTime = System.currentTimeMillis();
        Iterator<Map.Entry<String, Long>> it = mMap.entrySet().iterator();
        while (it.hasNext()) {
            Map.Entry<String, Long> entry = it.next();
            if ((currentTime - entry.getValue()) >= millisInFuture) {
                mMap.remove(entry.getKey());
                for (Map<String, OnNotifyTimeCallback> map : mCallbacks) {
                    if (map.containsKey(entry.getKey())) {
                        mCallbacks.remove(map);
                        break;
                    }
                }
            }
        }
    }

    class MyTimeTask extends TimerTask {

        @Override
        public void run() {
            check();
        }
    }
}
第二种实现
public class TimerHelper1 implements ITimer {

    private Map<String, TimeTask> mMap;
    private static TimerHelper1 instance;

    private static final long MILLIS_IN_FUTURE = 60 * 1000;
    private static final long COUNT_DOWN_INTERVAL = 1000;

    //写这个builder是为了方便单元测试滴时候去mock对象
    private TimeTaskBuilder timeTaskBuilder;

    //默认构造器
    private TimerHelper1() {
        mMap = new HashMap<>();
        timeTaskBuilder = new TimeTaskBuilder() {
            @Override
            public TimeTask build(String tag, long millisInFuture, long countDownInterval) {
                return new TimeTask(millisInFuture, countDownInterval, tag);
            }
        };
    }

    public static TimerHelper1 getInstance() {
        if (instance == null) {
            synchronized (TimerHelper1.class) {
                if (instance == null) {
                    instance = new TimerHelper1();
                }
            }
        }
        return instance;
    }

    public static void setupTimeTaskBuilder(TimeTaskBuilder builder) {
        instance.timeTaskBuilder = builder;
    }

    /**
     * onResume或者onstart方法中调用
     * @param str 页面的key
     * @param onNotifyTimeCallback 回调
     */
    @Override
    public void bind(String str, OnNotifyTimeCallback onNotifyTimeCallback) {
        if (mMap.containsKey(str)) {
            TimeTask timeTask = mMap.get(str);
            timeTask.setOnNotifyTimeCallback(onNotifyTimeCallback);
            onNotifyTimeCallback.onNotify(timeTask.getMillis());
        }
    }
    @Override
    public void start(String str, OnNotifyTimeCallback onNotifyTimeCallback) {
        start(str, onNotifyTimeCallback, MILLIS_IN_FUTURE, COUNT_DOWN_INTERVAL);
    }

    /**
     * 任务启动调用
     * @param name 页面的key
     * @param onNotifyTimeCallback 回调
     * @param millisInFuture 总时间
     * @param countDownInterval 间隔时间
     */
    @Override
    public void start(String name, final OnNotifyTimeCallback onNotifyTimeCallback,
                      long millisInFuture, long countDownInterval) {
        final TimeTask timeTask = timeTaskBuilder.build(name, millisInFuture, countDownInterval);

        timeTask.setOnNotifyTimeCallback(new OnNotifyTimeCallback() {
            @Override
            public void onNotify(long time) {
                if (time == 0) {
                    mMap.remove(timeTask.tag);
                }
                Log.d("DEBUG_DEBUG", mMap.size() + "");
                onNotifyTimeCallback.onNotify(time);
            }
        });
        mMap.put(name, timeTask);
        timeTask.start();
    }

    /**
     * 移除回调
     * @param string
     */
    @Override
    public void unbind(String string) {
        if (mMap.containsKey(string)) {
            mMap.get(string).setOnNotifyTimeCallback(null);
        }
    }

    public static class TimeTask extends CountDownTimer {

        private long millis;
        private String tag;

        public void setOnNotifyTimeCallback(OnNotifyTimeCallback onNotifyTimeCallback) {
            mOnNotifyTimeCallback = onNotifyTimeCallback;
        }

        public OnNotifyTimeCallback getOnNotifyTimeCallback() {
            return mOnNotifyTimeCallback;
        }

        private OnNotifyTimeCallback mOnNotifyTimeCallback;


        /**
         * @param millisInFuture    The number of millis in the future from the call
         *                          to {@link #start()} until the countdown is done and {@link #onFinish()}
         *                          is called.
         * @param countDownInterval The interval along the way to receive
         *                          {@link #onTick(long)} callbacks.
         */
        public TimeTask(long millisInFuture, long countDownInterval, String str) {
            super(millisInFuture, countDownInterval);
            this.millis = millisInFuture;
            this.tag = str;
        }

        /**
         * 每隔一秒(默认)会进行回调
         * @param millisUntilFinished
         */
        @Override
        public void onTick(long millisUntilFinished) {
            millis = millisUntilFinished / 1000;
            if (mOnNotifyTimeCallback != null) {
                mOnNotifyTimeCallback.onNotify(millis);
            }
        }

        /**
         * 倒计时完成后回调
         */
        @Override
        public void onFinish() {
            if (mOnNotifyTimeCallback != null) {
                mOnNotifyTimeCallback.onNotify(0);
            }
        }

        public long getMillis() {
            return millis;
        }
    }

    public interface TimeTaskBuilder {
        TimeTask build(String tag, long millisInFuture, long countDownInterval);
    }
}
第三种实现方式就大家自己做了
下面写一下调用方式

1、 实现OnNotifyTimeCallback接口并在onCreate中实例化对象

 ITimer mITimer;
 mITimer = TimerHelper.getInstance();

2、 在onResume或者onStart中bind回调

@Override
    protected void onResume() {
        super.onResume();
        mITimer.bind(getLocalClassName(), this);
    }

3、 在onPause或者onStop中unbind回调

 @Override
    protected void onPause() {
        super.onPause();
        mITimer.unbind(getLocalClassName());
    }

4、在需要的事件中发起任务(我这里写入的是倒计时10s,每隔1s执行,也可以不用穿这两个参数,默认为60s,每隔1s执行)

button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mITimer.start(getLocalClassName(), MainActivity.this, 10000, 1000);
            }
        });

5、在onNotify里面执行更新操作

 @Override
    public void onNotify(long time) {
        remainTime = time;
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                if (remainTime == -1L || remainTime == 0L) {
                    button.setText("10");
                    button.setClickable(true);
                    button.setBackgroundColor(Color.GREEN);
                } else {
                    button.setText(remainTime + "");
                    button.setClickable(false);
                    button.setBackgroundColor(Color.GRAY);
                }
            }
        });
    }

如果要切换实现方式 直接在 mITimer = TimerHelper.getInstance();这里选择要实现的方式就好了,如果还有其他方式,直接实现ITmer接口,然后实现那几个方法就好了。

效果图

android 定时器