安卓计时器

java自定义计时器


前言

用android官方自带的CountDownTimer计时器的时候安卓六和安卓十的源码有差别,导致我的计时一直有一些小bug

所以就自己写了个自用的计时器,方便以后查阅


提示:以下是本篇文章正文内容,下面案例可供参考

一、模仿countdownTimer来自定义一个自己的计时器

源码如下:

package com.chuyitech.gourmagic.device.utils;

import android.os.Handler;
import android.os.Looper;
import android.os.Message;

import com.chuyitech.gourmagic.device.views.base.BaseThread;

public abstract class ChuyiCountDownTimer {

    /**
     * Millis since epoch when alarm should stop.
     */
    private final long mMillisInFuture;

    private  long mCurrentTime;

    /**
     * The interval in millis that the user receives callbacks
     */
    private final long mCountdownInterval;

    /**
     * boolean representing if the timer was cancelled
     */
    private boolean mCancelled = false;

    private Thread thread;

    /**
     * @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 ChuyiCountDownTimer(long millisInFuture, long countDownInterval) {
        if(millisInFuture==0||millisInFuture%1000!=0||countDownInterval==0||countDownInterval%1000!=0){
            throw new RuntimeException("参数不合法");
        }
        mMillisInFuture = millisInFuture;
        mCountdownInterval = countDownInterval;
    }

    /**
     * Cancel the countdown.
     */
    public synchronized final void cancel() {
        mCancelled = true;
        mHandler.removeMessages(MSG);
        if(thread!=null && thread.isAlive()){
            thread.interrupt();
            thread=null;
        }
    }

    /**
     * Start the countdown.
     */
    public synchronized final ChuyiCountDownTimer start() {
        mCancelled = false;
        if (mMillisInFuture <= 0) {
            onFinish();
            return this;
        }

        mCurrentTime=mMillisInFuture;
        if(thread==null||!thread.isAlive()){
            thread=new BaseThread(){
                @Override
                public void run() {
                    super.run();
                    while (mCurrentTime>=0 && !isInterrupted()){
                        mCurrentTime-=1000;
                        ProgramUtils.sleep(1000);
                        //识别结束标识
                        if(mCurrentTime<=0){
                            mHandler.sendMessage(mHandler.obtainMessage(MSG));
                        }
                        //识别倒计时(第一次降低到时间以内不上报)
                        if((mMillisInFuture-mCurrentTime)%mCountdownInterval==0 ){
                            mHandler.sendMessage(mHandler.obtainMessage(MSG));
                        }
                    }
                }
            };
            thread.start();
        }
        return this;
    }


    /**
     * Callback fired on regular interval.
     * @param millisUntilFinished The amount of time until finished.
     */
    public abstract void onTick(long millisUntilFinished);

    /**
     * Callback fired when the time is up.
     */
    public abstract void onFinish();


    private static final int MSG = 1;


    // handles counting down
    private Handler mHandler = new Handler(Looper.getMainLooper()) {

        @Override
        public void handleMessage(Message msg) {

            synchronized (this) {
                if (mCancelled) {
                    return;
                }
                if (mCurrentTime <= 0) {
                    onFinish();
                } else {
                    onTick(mCurrentTime);
                }
            }
        }
    };
}

由上面源码可以看出这是个抽象类,类中含有两个抽象方法

 /**
     * Callback fired on regular interval.
     * @param millisUntilFinished The amount of time until finished.
     */
    public abstract void onTick(long millisUntilFinished);

    /**
     * Callback fired when the time is up.
     */
    public abstract void onFinish();

含有一个成员方法:

 /**
     * Start the countdown.
     */
    public synchronized final ChuyiCountDownTimer start() {
        mCancelled = false;
        if (mMillisInFuture <= 0) {
            onFinish();
            return this;
        }

        mCurrentTime=mMillisInFuture;
        if(thread==null||!thread.isAlive()){
            thread=new BaseThread(){
                @Override
                public void run() {
                    super.run();
                    while (mCurrentTime>=0 && !isInterrupted()){
                        mCurrentTime-=1000;
                        ProgramUtils.sleep(1000);
                        //识别结束标识
                        if(mCurrentTime<=0){
                            mHandler.sendMessage(mHandler.obtainMessage(MSG));
                        }
                        //识别倒计时(第一次降低到时间以内不上报)
                        if((mMillisInFuture-mCurrentTime)%mCountdownInterval==0 ){
                            mHandler.sendMessage(mHandler.obtainMessage(MSG));
                        }
                    }
                }
            };
            thread.start();
        }
        return this;
    }

具体方法中在工作线程中调用了mHandler,之后主线程收到消息,根据条件执行onTick()和onFinish()方法,

private static final int MSG = 1;


    // handles counting down
    private Handler mHandler = new Handler(Looper.getMainLooper()) {

        @Override
        public void handleMessage(Message msg) {

            synchronized (this) {
                if (mCancelled) {
                    return;
                }
                if (mCurrentTime <= 0) {
                    onFinish();
                } else {
                    onTick(mCurrentTime);
                }
            }
        }
    };

根据上面可以看出我们实现这个抽象类后,调用start方法,start方法就可以通过handler传递消息调用实现类中实现的onTick()和onFinish()方法,(解释下为何这里用handler,因为计时器在子线程中,而onTick/onFinish实现方法中的内容是和主线程有关的),

至于上面如何实现的计时器功能,因为start方法中子线程在未满足条件前一直在执行循环,同时子线程中在执行线程休眠1s的逻辑,故而计时器是按照1s来递减的。

同时该自定义计时器提供给外部cancel方法,

/**
     * Cancel the countdown.
     */
    public synchronized final void cancel() {
        mCancelled = true;
        mHandler.removeMessages(MSG);
        if(thread!=null && thread.isAlive()){
            thread.interrupt();
            thread=null;
        }
    }

其中的thread.interrupt();可以让start方法中

while (mCurrentTime>=0 && !isInterrupted())

循环条件不满足,从而退出计时器

二、使用步骤

在需要调用计时器的类中加上以下代码:

 /*开始计时*/
        if (timer != null) {
            timer.cancel();
            timer = null;
        }
        timer = new ChuyiCountDownTimer(reserveTime.get() * 1000, 60 * 1000) {
            public void onTick(long millisUntilFinished) {
                if (!isCancel) {
                    reserveTime.set(reserveTime.get() - 60);
                    Global.reserveTime = reserveTime.get();

                    changeShowTimeAboutReserve();
                }
            }

            public void onFinish() {
                if (!isCancel) {
                    reserveTime.set(-1);
                    Global.reserveTime = reserveTime.get();
                    remind.set(free.getContent());
                    if (!DeviceState.isIsRun() && status.get().equals("running"))
                        handler.sendEmptyMessage(MESSAGE_BEGIN);
                }
            }
        };
        timer.start();

上面代码只需关注

/*开始计时*/
        if (timer != null) {
            timer.cancel();
            timer = null;
        }
        timer = new ChuyiCountDownTimer(reserveTime.get() * 1000, 60 * 1000) {
            public void onTick(long millisUntilFinished) {
//                if (!isCancel) {
//                    reserveTime.set(reserveTime.get() - 60);
//                    Global.reserveTime = reserveTime.get();
//
//                    changeShowTimeAboutReserve();
//                }
            }

            public void onFinish() {
//                if (!isCancel) {
//                    reserveTime.set(-1);
//                    Global.reserveTime = reserveTime.get();
//                    remind.set(free.getContent());
//                    if (!DeviceState.isIsRun() && status.get().equals("running"))
//                        handler.sendEmptyMessage(MESSAGE_BEGIN);
//                }
            }
        };
        timer.start();

其中

timer = new ChuyiCountDownTimer(reserveTime.get() * 1000, 60 * 1000)

中的reserveTime.get()是个秒数值,是个int型,*1000,结果为毫秒,这个参数的意义是倒计时总时长,第二个参数是回调onTick函数的间隔时间长,60 * 1000则代表60s就调用一次onTick方法,

//识别结束标识
                        if(mCurrentTime<=0){
                            mHandler.sendMessage(mHandler.obtainMessage(MSG));
                        }
                        //识别倒计时(第一次降低到时间以内不上报)
                        if((mMillisInFuture-mCurrentTime)%mCountdownInterval==0 ){
                            mHandler.sendMessage(mHandler.obtainMessage(MSG));
                        }

看,计时器中的start方法当mCurrentTime<=0,即计时结束时会发送一次handler消息,第二个当mMillisInFuture-mCurrentTime可以整除mCountdownInterval(即上面的传参60*1000)时就会发送一次handler消息,

if (mCurrentTime <= 0) {
                    onFinish();
                } else {
                    onTick(mCurrentTime);
                }

由上面可以看出,外部调用自定义计时器类时传入两个参数,第一个参数为总时长,第二个参数为onTick调用的间隔时长。
总时长mCurrentTime <= 0就调用onFinish,间隔时长每到达一次mMillisInFuture-mCurrentTime)%mCountdownInterval==0即调用onTick方法,这两个方法在外部会重写(你可以加入自己想重写的逻辑),

三、总结

我为什么不用安卓自带的countdownTimer呢,因为当时客户群体中有安卓六手机,安卓六手机使用自带的计时器时会出现一种情况,用户设置了倒计时30分钟,用户刚刚设置完ui就显示成了29分钟(因为此时用户设置完那一刻已经过了一s,倒计时只剩29min59s了),但是因为ui图上没有秒数的ui,所以导致用户设置完时间,就立刻变成了少一分钟,非常的不合适,安卓六以上系统的计时器没这个问题,不得已自定义了个计时器,解决了这个bug。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值