(七十六) CountDownTimer

2023-12-19 07:48
文章标签 countdowntimer 七十六

本文主要是介绍(七十六) CountDownTimer,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

demo:https://github.com/happyjiatai/demo_csdn/tree/master/demo_76_countdowntimer

1. CountDownTimer简介

源码上是这么解释的:Schedule a countdown until a time in the future, with regular notifications on intervals along the way.翻译一下就是说设一个定时器,每隔固定间隔提醒一下。看了api,其实这个类在定时的时间到了以后也会通知一下用户。

 

2. CountDownTimer demo

demo(源码示例):

package com.example.demo_76_countdowntimer;import android.os.Bundle;
import android.os.CountDownTimer;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.widget.TextView;public class MainActivity extends AppCompatActivity {private static final String TAG = "countdowntimer";@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);final TextView mTextField = findViewById(R.id.text);new CountDownTimer(15000, 1000) {public void onTick(long millisUntilFinished) {Log.d(TAG, "millisUntilFinished: " + millisUntilFinished);mTextField.setText("seconds remaining: " + millisUntilFinished / 1000);}public void onFinish() {Log.d(TAG, "onFinish() ");mTextField.setText("done!");}}.start();}
}

效果:会从14依次减少,最后变为done!。

log:

07-27 19:59:29.346 19686-19686/com.example.demo_76_countdowntimer D/countdowntimer: millisUntilFinished: 14992
07-27 19:59:30.346 19686-19686/com.example.demo_76_countdowntimer D/countdowntimer: millisUntilFinished: 13992
07-27 19:59:31.346 19686-19686/com.example.demo_76_countdowntimer D/countdowntimer: millisUntilFinished: 12992
07-27 19:59:32.348 19686-19686/com.example.demo_76_countdowntimer D/countdowntimer: millisUntilFinished: 11990
07-27 19:59:33.350 19686-19686/com.example.demo_76_countdowntimer D/countdowntimer: millisUntilFinished: 10988
07-27 19:59:34.354 19686-19686/com.example.demo_76_countdowntimer D/countdowntimer: millisUntilFinished: 9984
07-27 19:59:35.358 19686-19686/com.example.demo_76_countdowntimer D/countdowntimer: millisUntilFinished: 8980
07-27 19:59:36.360 19686-19686/com.example.demo_76_countdowntimer D/countdowntimer: millisUntilFinished: 7978
07-27 19:59:37.364 19686-19686/com.example.demo_76_countdowntimer D/countdowntimer: millisUntilFinished: 6974
07-27 19:59:38.369 19686-19686/com.example.demo_76_countdowntimer D/countdowntimer: millisUntilFinished: 5969
07-27 19:59:39.371 19686-19686/com.example.demo_76_countdowntimer D/countdowntimer: millisUntilFinished: 4967
07-27 19:59:40.375 19686-19686/com.example.demo_76_countdowntimer D/countdowntimer: millisUntilFinished: 3963
07-27 19:59:41.379 19686-19686/com.example.demo_76_countdowntimer D/countdowntimer: millisUntilFinished: 2959
07-27 19:59:42.382 19686-19686/com.example.demo_76_countdowntimer D/countdowntimer: millisUntilFinished: 1956
07-27 19:59:43.385 19686-19686/com.example.demo_76_countdowntimer D/countdowntimer: millisUntilFinished: 953
07-27 19:59:44.341 19686-19686/com.example.demo_76_countdowntimer D/countdowntimer: onFinish() 

 

3. 源码分析

/*** 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 downprivate Handler mHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {synchronized (CountDownTimer.this) {if (mCancelled) {return;}final long millisLeft = mStopTimeInFuture - SystemClock.elapsedRealtime();if (millisLeft <= 0) {onFinish();} else {long lastTickStart = SystemClock.elapsedRealtime();onTick(millisLeft);// take into account user's onTick taking time to executelong lastTickDuration = SystemClock.elapsedRealtime() - lastTickStart;long delay;if (millisLeft < mCountdownInterval) {// just delay until donedelay = millisLeft - lastTickDuration;// special case: user's onTick took more than interval to// complete, trigger onFinish without delayif (delay < 0) delay = 0;} else {delay = mCountdownInterval - lastTickDuration;// special case: user's onTick took more than interval to// complete, skip to next intervalwhile (delay < 0) delay += mCountdownInterval;}sendMessageDelayed(obtainMessage(MSG), delay);}}}};
}

源码实现其实一目了然,就是每隔某段时间发送一个消息回调onTick()方法,时间走完后回调onFinish()方法。

onTick()和onFinish()方法是抽象类,供调用方实现。

3.1 构造函数

指定定时时长和调用onTick时间间隔

    /*** @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;}

3.2 start()

计算一下停止时间,然后开始发送消息供handler处理。

    /*** 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;}

3.3 cancel

停止消息处理

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

3.4 handler

  // handles counting downprivate Handler mHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {synchronized (CountDownTimer.this) {if (mCancelled) {return;}final long millisLeft = mStopTimeInFuture - SystemClock.elapsedRealtime();if (millisLeft <= 0) {onFinish();} else {long lastTickStart = SystemClock.elapsedRealtime();onTick(millisLeft);// take into account user's onTick taking time to executelong lastTickDuration = SystemClock.elapsedRealtime() - lastTickStart;long delay;if (millisLeft < mCountdownInterval) {// just delay until donedelay = millisLeft - lastTickDuration;// special case: user's onTick took more than interval to// complete, trigger onFinish without delayif (delay < 0) delay = 0;} else {delay = mCountdownInterval - lastTickDuration;// special case: user's onTick took more than interval to// complete, skip to next intervalwhile (delay < 0) delay += mCountdownInterval;}sendMessageDelayed(obtainMessage(MSG), delay);}}}};

这个handler是直接new处理的,说明是运行在新建线程的,demo是运行在主线程中,这意味着

  • onTick和onFinish不能执行耗时操作
  • mHandler持有Context的引用,如果退出应用时不调用cancel,容易造成内存泄露

两个操作可以证实内存泄露问题:

1.打开demo然后点击返回键,发现log仍然继续打印

2.点击返回键后再点击demo 图标,发现会有两个计时器在一起跑。

07-27 20:15:00.475 451-29656/? W/PackageManager: Failure retrieving resources for com.example.demo_76_countdowntimer: Resource ID #0x7f0a0000
07-27 20:15:04.752 20938-20938/com.example.demo_76_countdowntimer D/countdowntimer: Thread[main,5,main] millisUntilFinished: 14981
07-27 20:15:05.753 20938-20938/com.example.demo_76_countdowntimer D/countdowntimer: Thread[main,5,main] millisUntilFinished: 13980
07-27 20:15:06.754 20938-20938/com.example.demo_76_countdowntimer D/countdowntimer: Thread[main,5,main] millisUntilFinished: 12979
07-27 20:15:07.758 20938-20938/com.example.demo_76_countdowntimer D/countdowntimer: Thread[main,5,main] millisUntilFinished: 11976
07-27 20:15:08.759 20938-20938/com.example.demo_76_countdowntimer D/countdowntimer: Thread[main,5,main] millisUntilFinished: 10975
07-27 20:15:09.760 20938-20938/com.example.demo_76_countdowntimer D/countdowntimer: Thread[main,5,main] millisUntilFinished: 9974
07-27 20:15:10.765 20938-20938/com.example.demo_76_countdowntimer D/countdowntimer: Thread[main,5,main] millisUntilFinished: 8969
07-27 20:15:11.766 20938-20938/com.example.demo_76_countdowntimer D/countdowntimer: Thread[main,5,main] millisUntilFinished: 7968
07-27 20:15:12.771 20938-20938/com.example.demo_76_countdowntimer D/countdowntimer: Thread[main,5,main] millisUntilFinished: 6963
07-27 20:15:13.773 20938-20938/com.example.demo_76_countdowntimer D/countdowntimer: Thread[main,5,main] millisUntilFinished: 5961
07-27 20:15:14.803 20938-20938/com.example.demo_76_countdowntimer D/countdowntimer: Thread[main,5,main] millisUntilFinished: 4930
07-27 20:15:15.149 20938-20938/com.example.demo_76_countdowntimer D/countdowntimer: Thread[main,5,main] millisUntilFinished: 14987
07-27 20:15:15.804 20938-20938/com.example.demo_76_countdowntimer D/countdowntimer: Thread[main,5,main] millisUntilFinished: 3929
07-27 20:15:16.149 20938-20938/com.example.demo_76_countdowntimer D/countdowntimer: Thread[main,5,main] millisUntilFinished: 13987
07-27 20:15:16.805 20938-20938/com.example.demo_76_countdowntimer D/countdowntimer: Thread[main,5,main] millisUntilFinished: 2928
07-27 20:15:17.149 20938-20938/com.example.demo_76_countdowntimer D/countdowntimer: Thread[main,5,main] millisUntilFinished: 12987
07-27 20:15:17.806 20938-20938/com.example.demo_76_countdowntimer D/countdowntimer: Thread[main,5,main] millisUntilFinished: 1927
07-27 20:15:18.151 20938-20938/com.example.demo_76_countdowntimer D/countdowntimer: Thread[main,5,main] millisUntilFinished: 11985
07-27 20:15:18.809 20938-20938/com.example.demo_76_countdowntimer D/countdowntimer: Thread[main,5,main] millisUntilFinished: 924
07-27 20:15:19.156 20938-20938/com.example.demo_76_countdowntimer D/countdowntimer: Thread[main,5,main] millisUntilFinished: 10980
07-27 20:15:19.737 20938-20938/com.example.demo_76_countdowntimer D/countdowntimer: onFinish() 
07-27 20:15:20.160 20938-20938/com.example.demo_76_countdowntimer D/countdowntimer: Thread[main,5,main] millisUntilFinished: 9976
07-27 20:15:21.164 20938-20938/com.example.demo_76_countdowntimer D/countdowntimer: Thread[main,5,main] millisUntilFinished: 8973
07-27 20:15:22.166 20938-20938/com.example.demo_76_countdowntimer D/countdowntimer: Thread[main,5,main] millisUntilFinished: 7970
07-27 20:15:23.171 20938-20938/com.example.demo_76_countdowntimer D/countdowntimer: Thread[main,5,main] millisUntilFinished: 6966
07-27 20:15:24.175 20938-20938/com.example.demo_76_countdowntimer D/countdowntimer: Thread[main,5,main] millisUntilFinished: 5961
07-27 20:15:25.180 20938-20938/com.example.demo_76_countdowntimer D/countdowntimer: Thread[main,5,main] millisUntilFinished: 4957
07-27 20:15:26.181 20938-20938/com.example.demo_76_countdowntimer D/countdowntimer: Thread[main,5,main] millisUntilFinished: 3955
07-27 20:15:27.186 20938-20938/com.example.demo_76_countdowntimer D/countdowntimer: Thread[main,5,main] millisUntilFinished: 2951
07-27 20:15:28.190 20938-20938/com.example.demo_76_countdowntimer D/countdowntimer: Thread[main,5,main] millisUntilFinished: 1947
07-27 20:15:29.193 20938-20938/com.example.demo_76_countdowntimer D/countdowntimer: Thread[main,5,main] millisUntilFinished: 943
07-27 20:15:30.139 20938-20938/com.example.demo_76_countdowntimer D/countdowntimer: onFinish() 

接下来看下handeMessage是如何处理的:

1)加锁,考虑到多线程情况

2)cancel方法不是说只通过移除消息肯定会取消下一次操作的,这里加了个保险,如果cancel为true,则不继续执行。

3)计算下剩余时间,到了就回调onFinish()方法。

                final long millisLeft = mStopTimeInFuture - SystemClock.elapsedRealtime();if (millisLeft <= 0) {onFinish();}

4)时间没到的话,以执行onTick方法的时长为依据设定消息发送延迟。

如果剩余时长小于时间间隔,那么delay为剩余时长减去onTick方法的执行时长,如果小于0,则delay为0。

如果剩余时长大于时间间隔,那么delay为时间间隔减去onTick方法的执行时长,如果小于0则补时间间隔,直到大于0。

举个例子:

总时长15s,间隔1s(mCountdownInterval),执行onTick方法2.5s

millisLeft = 15s

先花费2.5s执行onTick方法,lastTickDuration=2.5s,剩余12.5s

走进if判断,由于millisLeft>mCountdownInterval,走进下面的else

1 - 2.5 = -1.5s,-1.5+1+1=0.5,下次消息将在0.5s后发出。

这样来看是3s发送一次消息。

 

例子举完总结一下,就是剩余时间不够一次间隔了,那么就从消息处理开始等剩余时间走完发送最后的消息;如果剩余时间够一次间隔,就从消息处理开始等n个间隔发送消息,n取决于onTick的执行时长是时间间隔的多少倍向上取整。

 

                } else {long lastTickStart = SystemClock.elapsedRealtime();onTick(millisLeft);// take into account user's onTick taking time to executelong lastTickDuration = SystemClock.elapsedRealtime() - lastTickStart;long delay;if (millisLeft < mCountdownInterval) {// just delay until donedelay = millisLeft - lastTickDuration;// special case: user's onTick took more than interval to// complete, trigger onFinish without delayif (delay < 0) delay = 0;} else {delay = mCountdownInterval - lastTickDuration;// special case: user's onTick took more than interval to// complete, skip to next intervalwhile (delay < 0) delay += mCountdownInterval;}sendMessageDelayed(obtainMessage(MSG), delay);}

 

4. 总结

使用CountDownTimer可以实现一些简单的固定间隔操作,定时结束后完成特定目标的需求(固定间隔定的是1s,但有可能是2s一刷新,取决于onTick执行时长),另外要注意其如果是在UI线程new出来的,那么onTick和onFinish不要执行耗时操作。CountDownTimer提前结束或者activity异常退出记得调用它的cancel方法,不然会有内存泄露。正常情况下使用handler请使用静态内部类加虚引用来规避内存泄露的风险。

这篇关于(七十六) CountDownTimer的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/511424

相关文章

Android 使用CountDownTimer实现倒计时的功能

Android 使用CountDownTimer实现倒计时的功能 “`package com.alex.countdowntimer; import java.text.SimpleDateFormat; import java.util.Date; import android.app.Activity; import android.os.Bundle; import androi

Android音频进阶之1.0到14.0音频焦点变化(七十六)

简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 优质专栏:多媒体系统工程师系列【原创干货持续更新中……】🚀 优质视频课程:AAOS车载系统+AOSP14系统攻城狮入门实战课【原创干货持续更新中……】🚀 人生格言:

android的倒计时类CountDownTimer使用

private CountDownTimer timer = new CountDownTimer(30000, 1000) {@Overridepublic void onTick(long millisUntilFinished) {mCountTv.setText((millisUntilFinished / 1000) + "″后重发");}@Overridepublic void onF

CountDownTimer使用

CountDownTimer类介绍 CountDownTimer类比较简单,总共就一个构造和4个方法。内部是通过handler实现 CountDownTimer(long time,long interval):参数time是总时间,interval是间隔时间 start():开始倒计时的方法 cancel():取消倒计时的方法 onTink(long time):抽象方法,每个间隔时间

【七十六】【算法分析与设计】2435. 矩阵中和能被 K 整除的路径,87. 扰乱字符串,三维动态规划

2435. 矩阵中和能被 K 整除的路径 给你一个下标从 0 开始的 m x n 整数矩阵 grid 和一个整数 k 。你从起点 (0, 0) 出发,每一步只能往 下 或者往 右 ,你想要到达终点 (m - 1, n - 1) 。 请你返回路径和能被 k 整除的路径数目,由于答案可能很大,返回答案对 10(9)7 取余 的结果。 示例 1: 输入:grid = [[5,2,4],[3,0

Android 系统锁屏息屏休眠时Handler CountDownTimer计时器停止运行问题解决

1.前言 在进行app开发的过程中,在进行某些倒计时的功能项目开发中,会遇到在锁屏息屏休眠一段时间的情况下, 在唤醒屏幕的情况下发现倒计时已经停止了,这是因为在系统处于休眠的状态下cpu也停止了工作,所以 handler和countdowntimer倒计时也停止了工作,接下来就来看怎么样来实现这个问题的解决 2.系统锁屏息屏休眠时Handler CountDownTimer计时器停止运行问题解

CountDownTimer 倒计时不准确问题解决

前言 最近笔者在实现一共和倒计时有关的功能,使用CountDownTimer实现。然而,在测试的时候发现,倒计时经常发现跳秒、不出现1的情况,因此对这方面进行了一些了解。本文准备介绍一下CountDownTimer倒计时不准确的原因,以及提出一些解决方案~ CountDownTimer的问题 发现跳秒、不出现1的问题之后,首先将每次onTick()的回调时间打印了出来(这里忘记保存log

[Python从零到壹] 七十六.图像识别及经典案例篇之OpenCV快速实现人脸检测及视频人脸动态识别

欢迎大家来到“Python从零到壹”,在这里我将分享约200篇Python系列文章,带大家一起去学习和玩耍,看看Python这个有趣的世界。所有文章都将结合案例、代码和作者的经验讲解,真心想把自己近十年的编程经验分享给大家,希望对您有所帮助,文章中不足之处也请海涵。Python系列整体框架包括基础语法10篇、网络爬虫30篇、可视化分析10篇、机器学习20篇、大数据分析20篇、图像识别30篇、人工智

Java 学习之路 之 线程同步(七十六)

多线程编程是有趣的事情,它很容易突然出现 “错误情况”,这是由系统的线程调度具有一定的随机性造成的,不过即使程序偶然出现问题,那也是由于编程不当引起的。当使用多个线程来访问同一个数据时,很容易 “偶然” 出现线程安全问题。 1,线程安全问题 关于线程安全问题,有一个经典的问题——银行取钱的问题。银行取钱的基本流程基本上可以分为如下几个步骤。 (1)用户输入账户、密码,系统判断用户的账户、密码

Android CountDownTimer的使用

最近博主需要实现一个倒计时相关的功能,被推荐了Android的CountDownTimer工具类,在此说一下CountDownTimer的使用以及源码的解读 以下是一个总计10秒倒计时,每间隔1秒进行回调的例子: CountDownTimer timer = new CountDownTimer(10000, 1000) {@Overridepublic void onTi