countdown倒计时安卓软件_倒计时显示装置设计

countdown倒计时安卓软件_倒计时显示装置设计实现原理拿CountDownTimer的源代码看一下,并不复杂,基本上是对Handler的封装,使用send/postdelay。这套机制仍然首先于Handler的原理,所以在精度上也不能够保证很精确,只能保证不会早于预期执行。详见我另外一篇介绍Handlersend/postdelay的文章:HandlersendMessageDelayed()/postDelayed()机制详解。源…

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

实现原理

拿CountDownTimer的源代码看一下,并不复杂,基本上是对Handler的封装,使用send/post delay。这套机制仍然首先于Handler的原理,所以在精度上也不能够保证很精确,只能保证不会早于预期执行。详见我另外一篇介绍Handler send/post delay的文章: Handler sendMessageDelayed()/postDelayed()机制详解

源代码:

/*
 * Copyright (C) 2008 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.os;

/**
 * Schedule a countdown until a time in the future, with
 * regular notifications on intervals along the way.
 *
 * Example of showing a 30 second countdown in a text field:
 *
 * <pre class="prettyprint">
 * new CountDownTimer(30000, 1000) {
 *
 *     public void onTick(long millisUntilFinished) {
 *         mTextField.setText("seconds remaining: " + millisUntilFinished / 1000);
 *     }
 *
 *     public void onFinish() {
 *         mTextField.setText("done!");
 *     }
 *  }.start();
 * </pre>
 *
 * The calls to {@link #onTick(long)} are synchronized to this object so that
 * one call to {@link #onTick(long)} won't ever occur before the previous
 * callback is complete.  This is only relevant when the implementation of
 * {@link #onTick(long)} takes an amount of time to execute that is significant
 * compared to the countdown interval.
 */
public abstract class CountDownTimer {

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

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

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

    /**
     * @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 CountDownTimer(long millisInFuture, long countDownInterval) {
        mMillisInFuture = millisInFuture;
        mCountdownInterval = countDownInterval;
    }

    /**
     * Cancel the countdown.
     */
    public synchronized final void cancel() {
        mCancelled = true;
        mHandler.removeMessages(MSG);
    }

    /**
     * Start the countdown.
     */
    public synchronized final CountDownTimer start() {
        mCancelled = false;
        if (mMillisInFuture <= 0) {
            onFinish();
            return this;
        }
        mStopTimeInFuture = SystemClock.elapsedRealtime() + mMillisInFuture;
        mHandler.sendMessage(mHandler.obtainMessage(MSG));
        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() {

        @Override
        public void handleMessage(Message msg) {

            synchronized (CountDownTimer.this) {
                if (mCancelled) {
                    return;
                }

                final long millisLeft = mStopTimeInFuture - SystemClock.elapsedRealtime();

                if (millisLeft <= 0) {
                    onFinish();
                } else if (millisLeft < mCountdownInterval) {
                    // no tick, just delay until done
                    sendMessageDelayed(obtainMessage(MSG), millisLeft);
                } else {
                    long lastTickStart = SystemClock.elapsedRealtime();
                    onTick(millisLeft);

                    // take into account user's onTick taking time to execute
                    long delay = lastTickStart + mCountdownInterval - SystemClock.elapsedRealtime();

                    // special case: user's onTick took more than interval to
                    // complete, skip to next interval
                    while (delay < 0) delay += mCountdownInterval;

                    sendMessageDelayed(obtainMessage(MSG), delay);
                }
            }
        }
    };
}

看其中两个关键点:

(1)Handler的创建

    // handles counting down
    private Handler mHandler = new Handler() {...};

作为一个私有的非静态成员变量,这段代码执行在构造方法中,也就是说,Handler是在调用构造方法(new)的线程中被创建,并且是通过不带参数的构造方法Handler()。从Handler的源代码可以看到,这种情况会调用到:

    /**
     * Use the {@link Looper} for the current thread with the specified callback interface
     * and set whether the handler should be asynchronous.
     *
     * Handlers are synchronous by default unless this constructor is used to make
     * one that is strictly asynchronous.
     *
     * Asynchronous messages represent interrupts or events that do not require global ordering
     * with respect to synchronous messages.  Asynchronous messages are not subject to
     * the synchronization barriers introduced by {@link MessageQueue#enqueueSyncBarrier(long)}.
     *
     * @param callback The callback interface in which to handle messages, or null.
     * @param async If true, the handler calls {@link Message#setAsynchronous(boolean)} for
     * each {@link Message} that is sent to it or {@link Runnable} that is posted to it.
     *
     * @hide
     */
    public Handler(Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {
            final Class<? extends Handler> klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                    (klass.getModifiers() & Modifier.STATIC) == 0) {
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                    klass.getCanonicalName());
            }
        }

        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

通过Looper.myLooper()得到Looper对象并且校验非空,空则抛出RE。Looper.myLooper():

    /**
     * Return the Looper object associated with the current thread.  Returns
     * null if the calling thread is not associated with a Looper.
     */
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

由此可见,普通工作线程因为没有准备好Looper,所以显然是不能直接创建CountDownTimer对象的。

(2)Handler的消息处理/分发

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

        @Override
        public void handleMessage(Message msg) {

            synchronized (CountDownTimer.this) {
                if (mCancelled) {
                    return;
                }

                final long millisLeft = mStopTimeInFuture - SystemClock.elapsedRealtime();

                if (millisLeft <= 0) {
                    onFinish();
                } else if (millisLeft < mCountdownInterval) {
                    // no tick, just delay until done
                    sendMessageDelayed(obtainMessage(MSG), millisLeft);
                } else {
                    long lastTickStart = SystemClock.elapsedRealtime();
                    onTick(millisLeft);

                    // take into account user's onTick taking time to execute
                    long delay = lastTickStart + mCountdownInterval - SystemClock.elapsedRealtime();

                    // special case: user's onTick took more than interval to
                    // complete, skip to next interval
                    while (delay < 0) delay += mCountdownInterval;

                    sendMessageDelayed(obtainMessage(MSG), delay);
                }
            }
        }
    };

handleMessage()方法:

首先做剩余时间计算:

final long millisLeft = mStopTimeInFuture – SystemClock.elapsedRealtime();

mStopTimeInFuture是在倒计时开始方法start()中计算得到:

mStopTimeInFuture = SystemClock.elapsedRealtime() + mMillisInFuture;

mMillisInFuture是CountDownTimer构造方法中传入的参数,即倒计时的时长。

所以millisLeft计算出的是当前handleMessage()被执行时候,距离倒计时结束的剩余时间。

接下来的逻辑:

(1)如果发现剩余时间<=0,意味着倒计时已经结束,直接调用onFinish()。这里注意到,可能是会超过最后时限(即<0的情况)才执行onFinish()。原因也可参见我另外一篇 Handler sendMessageDelayed()/postDelayed()机制详解

(2)否则,如果剩余时间不足一次onTick间隔,直接以剩余时间sendMessageDelayed()。

(3)否则(这条逻辑分支是最关键的逻辑,实现onTick的循环调用):

a)首先记下一个当前的时间戳,即本次执行onTick开始的时间;

b)执行onTick(),注意,这里是同步执行;

c)执行完onTick()以后,再看一下当前的时间情况

// take into account user’s onTick taking time to execute

long delay = lastTickStart + mCountdownInterval – SystemClock.elapsedRealtime();

因为不能够确认同步执行的onTick()会耗时多少,所以对当前时间需要校验:

// special case: user’s onTick took more than interval to
// complete, skip to next interval

while (delay < 0) delay += mCountdownInterval;

以onTick间隔作为时间单元向下跳,直到delay为非负值,也就是说还没有流失掉的onTick执行时间距离,并以这个值sendMessageDelayed()下一条Msg。

总结一下,可以看出:

(1)每次handleMessage的时候,实际上是以当前时间值为基础,计算一下剩余的时间并决定接下来的处理逻辑,这样做的目的显然是在现行Android Handler机制里面,无法保证以绝对准时的delay时间处理Msg。

(2)对于同步onTick的处理很严谨,考虑到了onTick可能的耗时操作。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/194105.html原文链接:https://javaforall.net

(0)
上一篇 2025年11月12日 下午1:22
下一篇 2025年11月12日 下午2:01


相关推荐

  • 在vue项目中添加看板娘

    在vue项目中添加看板娘在个人网站中如何加入好看的看板娘组件效果如下 首先下载 live2dw 组件 地址 链接 https pan baidu com s 1K1hixvM9 2ySMV674KQDt 提取码 rs8g 接下来将 live2dw 导入到 vue 项目的 static 目录下如上图所示在 public 文件夹下的 index html 中添加

    2026年3月18日
    2
  • C6000汇编语言实现乘法,C6000(四)-汇编.ppt[通俗易懂]

    C6000汇编语言实现乘法,C6000(四)-汇编.ppt[通俗易懂]C6000(四)-汇编BIT/TI第四讲汇编语言初步第四讲汇编语言初步目的:用汇编语言编写简单程序学习内容汇编代码的结构汇编程序的构成编写简单算法:y=mx+b一、汇编代码的构成label:||[cond]instruction.unitoperand;comment常用伪指令二、汇编程序的构成程序=数据结构+算法汇编程序…

    2022年6月23日
    27
  • 搓牙机调试位置讲解_hadoopjps进程不全

    搓牙机调试位置讲解_hadoopjps进程不全1、strace-p[进程号]strace-p1002297strace:Process1002297attachedfutex(0x7fcbb95f3f84,FUTEX_WAIT_PRIVATE,1,NULL可以看到死在了futex(0x7fcbb95f3f84,FUTEX_WAIT_PRIVATE,1,NULL

    2025年11月16日
    4
  • python一个中文占几个字节_中文在python中占几个字节

    python一个中文占几个字节_中文在python中占几个字节如果是 utf 8 编码 那么一个中文字符占用三个字节 一个英文字符占用一个字节 如果是 gbk 编码 那么一个中文字符占用两个字节 一个英文字符占用一个字节 如果是 utf 8 编码 那么一个中文包含繁体字等于三个字节 一个英文字符等于一个字节 如果是 gbk 编码 那么一个中文包含繁体字等于两个字节 一个英文字符等于一个字节 我们可以用如下方法来判断 中文和符号 print type 中文 encode

    2026年3月17日
    2
  • Headless模式_java throwable

    Headless模式_java throwableHeadless模式是在缺少显示屏、键盘或者鼠标时的系统配置。在java.awt.toolkit和java.awt.graphicsenvironment类中有许多方法,除了对字体、图形和打印的操作外还可以调用显示器、键盘和鼠标的方法。但是有一些类中,比如Canvas和Panel,可以在headless模式下执行。系统属性配置为了启用headless模式,需要使用setProperty方法去设置相…

    2025年8月9日
    10
  • pycharm入门使用——添加第三方库

    pycharm入门使用——添加第三方库为你的 pycharm 添加第三方库清华 https pypi tuna tsinghua edu cn simple 阿里云 http mirrors aliyun com pypi simple 中国科技大学 https pypi mirrors ustc edu cn simple 华中理工大学 http pypi hustunique com 山东理工大学 http pypi sdutlinux org 豆瓣 http pypi douban com simple

    2026年3月27日
    2

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号