一,概述

在项目开发中经常会用到倒计时这个功能,而Android也帮我们封装好了一个类CountDownTimer,给我们的开发带来了很大的方便;

二,API

CountDownTimer (long millisInFuture, long countDownInterval)
参数1,设置倒计时的总时间(毫秒)
参数2,设置每次减去多少毫秒
  • 1
  • 2
  • 3

三,基本用法

以App中获短信取验证码为例:

    private Button btn;private TextView vertifyView;  @Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);initView();}private void initView(){vertifyView =(TextView) findViewById(R.id.vertifyView);btn =(Button) findViewById(R.id.button);btn.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {//1,请求后台...//2,触发定时器刷新UI(启动代码最好放在请求后台回调成功之后)timer.start();}});}private CountDownTimer timer = new CountDownTimer(10000, 1000) {  @Override  public void onTick(long millisUntilFinished) {  vertifyView.setText((millisUntilFinished / 1000) + "秒后可重发");  }  @Override  public void onFinish() {  vertifyView.setEnabled(true);  vertifyView.setText("获取验证码");  }  };  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

ok~这样一个基本的CountDownTimer案例就完成了

四,存在的问题

CountDownTimer如果使用不当,常常会报空指针异常,甚至造成严重的内存泄漏 
5.0源码:

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 if (millisLeft < mCountdownInterval) {// 剩余时间小于一次时间间隔的时候,不再通知,只是延迟一下sendMessageDelayed(obtainMessage(MSG), millisLeft);} else {long lastTickStart = SystemClock.elapsedRealtime();onTick(millisLeft);// 处理用户onTick执行的时间long delay = lastTickStart + mCountdownInterval - SystemClock.elapsedRealtime();// 特殊情况:用户的onTick方法花费的时间比interval长,那么直接跳转到下一次intervalwhile (delay < 0) delay += mCountdownInterval;sendMessageDelayed(obtainMessage(MSG), delay);}}}};
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103

从源码中我们可以看出,CountDownTimer的内部实现是采用Handler机制,通过sendMessageDelayed延迟发送一条message到主线程的looper中,然后在自身中收到之后判断剩余时间,并发出相关回调,然后再次发出message的方式。

这样的方式其实是有一定弊端的,那就是如果在Activity或者Fragment被回收时并未调用CountDownTimer的cancel()方法结束自己,这个时候CountDownTimer的Handler方法中如果判断到当前的时间未走完,那么会继续调用

sendMessageDelayed(obtainMessage(MSG), delay);
  • 1

触发

onTick(millisLeft);
  • 1

当回调了Activity或者fragment中CountDownTimer的onTick方法时,Activity或者Fragment已经被系统回收,从而里面的变量被设置为Null,再调用

vertifyView.setText((millisUntilFinished / 1000) + "秒后可重发");  
  • 1

vertifyView为空,也就空指针了~ 
同时,CountDownTimer中的Handler方法还在继续执行,这一块空间始终无法被系统回收也就造成了内存泄漏问题。

五,总结

1,在CountDownTimer的onTick方法中记得判空

activity中if(!activity.isFinishing()){//doing something...}fragment中if(getActivity()!=null){//doing something...}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

2,在配合DialogFragment使用时,如果在onFinish()方法调用了 dismiss()方法让弹框消失,记得 判断getFragmentManager是否为空

    @Overridepublic void onFinish() {if(getFragmentManager()!=null){dismiss();}}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

3,在使用CountDownTimer时,在宿主Activity或fragment生命周期结束的时候,记得调用timer.cancle()方法

@Overridepublic void onDestroy() {if(timer!=null){timer.cancel();timer = null;}super.onDestroy();}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

遇到问题还是尽量先从控件的源码中寻找答案~相信源码是最好的老师O(∩_∩)O哈哈~

CountDownTimer浅析相关推荐

  1. CountDownTimer 实现验证码倒计时

    先看下完整的代码,如是使用入职过去即可 public class MainActivity extends AppCompatActivity {private TextView code;@Over ...

  2. CountDownTimer的简单使用

    下面是60秒的倒计时 activity public class MainActivity extends AppCompatActivity {private TextView time; @Ove ...

  3. 浅析 JavaScript 中的 函数 uncurrying 反柯里化

    柯里化 柯里化又称部分求值,其含义是给函数分步传递参数,每次传递参数后部分应用参数,并返回一个更具体的函数接受剩下的参数,这中间可嵌套多层这样的接受部分参数函数,直至返回最后结果. 因此柯里化的过程是 ...

  4. 浅析Python中bytes和str区别

    本博转载自:Chown-Jane-Y的浅析Python3中的bytes和str类型 Python 3最重要的新特性之一是对字符串和二进制数据流做了明确的区分.文本总是Unicode,由str类型表示, ...

  5. 学习《Linux设备模型浅析之设备篇》笔记(深挖一)

    这篇文章既然说了是浅析,那就是跳过了一些东西,我们把这些跳过的东西给它尽可能的补回来 今天登陆 lxr.free-electrons.com 发现内核版本已经升级到3.15了,那以后都使用3.15的源 ...

  6. 学习《Linux设备模型浅析之设备篇》笔记(一)

    最近在学习Linux设备模型,前面几篇文章也是读这篇的时候遇到问题,然后为了搞清楚先转去摸索才写出来的. 当然了,刚开始是先读到<Linux那些事儿之我是Sysfs>,搞不清楚才去读的&l ...

  7. 架构周报| 浅析MySQL JDBC连接配置上的两个误区

    经典案例 \\ 浅析MySQL JDBC连接配置上的两个误区:相信使用MySQL的同学都配置过它的JDBC驱动,多数人会直接从哪里贴一段URL过来,然后稍作修改就上去了,对应的连接池配置也是一样的,很 ...

  8. 超级账本(Hyperledger Fabric)之权限管理浅析

    链客,专为开发者而生,有问必答! 此文章来自区块链技术社区,未经允许拒绝转载. 超级账本(Hyperledger Fabric)之权限管理浅析 超级账本是联盟链的代表,而其相对于共链(例如比特币,以太 ...

  9. linux内核SMP负载均衡浅析

    需求       在<linux进程调度浅析>一文中提到,在SMP(对称多处理器)环境下,每个CPU对应一个run_queue(可执行队列).如果一个进程处于TASK_RUNNING状态( ...

最新文章

  1. ubuntu 在线安装mysql_Ubuntu下安装MySQL5.6
  2. 基于SSM实现个人健康管理系统
  3. Java控制语句——switch语句
  4. Hadoop JobHistory
  5. 【C/C++】C/C++博客汇总
  6. 51nod---无法表示的数
  7. EXCEL数据汇总-数据透视图
  8. 我的第一个wp8小程序
  9. 重启nginx后丢失nginx.pid的解决方法(转)
  10. BZOJ3261: 最大异或和(可持久化trie树)
  11. Tip:在使用AjaxControlTookit的控件时响应事件
  12. 往PPT幻灯中添加图片的方法有哪些?
  13. matlab开关电源仿真软件,开关电源仿真设计软件选择
  14. ios15验证失败因为你不在连接互联网
  15. 【关于四足机器人那些事】足端轨迹规划-复合摆线轨迹
  16. 致所有仍然关注BreezeMaphack的朋友
  17. 基于FPGA的电子计算器设计(下)
  18. phpstudy一直自动停止启动_window10 phpstudy2018 mysql服务重启之后自动停止
  19. bzoj3659 Which Dreamed It BEST定理(公式绝对没错doge)
  20. ​力扣解法汇总2315. 统计星号

热门文章

  1. 机器人操作——diffusion policy(2023)
  2. Linux 查看系统支持的最大的进程数量
  3. CorelDRAW快速制作雨天、雪天和雾天效果
  4. 计算机的硬件及其参数,计算机各种硬件参数介绍。(示例代码)
  5. Cocos2dx之精灵创建
  6. BEA:出色构建 高效整合——厦门地税税收征管系统实施案例
  7. 第3讲 Android Camera2 API 概述
  8. Fiddler 出现乱码的解决方法
  9. SQL server 外键约束操作
  10. nodejs+vue在线书店图书销售管理系统express