大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。
Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺
一,概述
在项目开发中经常会用到倒计时这个功能,而Android也帮我们封装好了一个类CountDownTimer,给我们的开发带来了很大的方便;
二,API
CountDownTimer (long millisInFuture, long countDownInterval)
参数1,设置倒计时的总时间(毫秒)
参数2,设置每次减去多少毫秒
- 1
- 2
- 3
三,基本用法
以App中获短信取验证码为例:
private Button btn;
private TextView vertifyView;
@Override
public 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() {
@Override
public 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 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) {
// 剩余时间小于一次时间间隔的时候,不再通知,只是延迟一下
sendMessageDelayed(obtainMessage(MSG), millisLeft);
} else {
long lastTickStart = SystemClock.elapsedRealtime();
onTick(millisLeft);
// 处理用户onTick执行的时间
long delay = lastTickStart + mCountdownInterval - SystemClock.elapsedRealtime();
// 特殊情况:用户的onTick方法花费的时间比interval长,那么直接跳转到下一次interval
while (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是否为空
@Override
public void onFinish() {
if(getFragmentManager()!=null){
dismiss();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
3,在使用CountDownTimer时,在宿主Activity或fragment生命周期结束的时候,记得调用timer.cancle()方法
@Override
public void onDestroy() {
if(timer!=null){
timer.cancel();
timer = null;
}
super.onDestroy();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
遇到问题还是尽量先从控件的源码中寻找答案~相信源码是最好的老师O(∩_∩)O哈哈~
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/192098.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...