CountDownTimer浅析
一,概述
在项目开发中经常会用到倒计时这个功能,而Android也帮我们封装好了一个类CountDownTimer,给我们的开发带来了很大的方便;
二,API
CountDownTimer (long millisInFuture, long countDownInterval)参数1,设置倒计时的总时间(毫秒)参数2,设置每次减去多少毫秒
三,基本用法
以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("获取验证码"); } };
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); } } } };}
从源码中我们可以看出,CountDownTimer的内部实现是采用Handler机制,通过sendMessageDelayed延迟发送一条message到主线程的looper中,然后在自身中收到之后判断剩余时间,并发出相关回调,然后再次发出message的方式。
这样的方式其实是有一定弊端的,那就是如果在Activity或者Fragment被回收时并未调用CountDownTimer的cancel()方法结束自己,这个时候CountDownTimer的Handler方法中如果判断到当前的时间未走完,那么会继续调用
sendMessageDelayed(obtainMessage(MSG), delay);
触发
onTick(millisLeft);
当回调了Activity或者fragment中CountDownTimer的onTick方法时,Activity或者Fragment已经被系统回收,从而里面的变量被设置为Null,再调用
vertifyView.setText((millisUntilFinished / 1000) + "秒后可重发");
vertifyView为空,也就空指针了~
同时,CountDownTimer中的Handler方法还在继续执行,这一块空间始终无法被系统回收也就造成了内存泄漏问题。
五,总结
1,在CountDownTimer的onTick方法中记得判空
activity中 if(!activity.isFinishing()){ //doing something... }fragment中 if(getActivity()!=null){ //doing something... }
2,在配合DialogFragment使用时,如果在onFinish()方法调用了 dismiss()方法让弹框消失,记得 判断getFragmentManager是否为空
@Override public void onFinish() { if(getFragmentManager()!=null){ dismiss(); } }
3,在使用CountDownTimer时,在宿主Activity或fragment生命周期结束的时候,记得调用timer.cancle()方法
@Override public void onDestroy() { if(timer!=null){ timer.cancel(); timer = null; } super.onDestroy(); }
遇到问题还是尽量先从控件的源码中寻找答案~相信源码是最好的老师O(∩_∩)O哈哈~
更多相关文章
- Android中Notification的framework层讲解【安卓源码解析四】
- Android ListView动画实现方法
- ADB连接Android设备的三种方法
- [Android设计模式]Android退出应用程序终极方法
- Android通过Alpha实现渐变效果的几个方法
- Android中AVD(Android Virtual Device)不能启动的处理方法
- Android多个Activity切换时其生命周期中的方法执行顺序