一,概述

在项目开发中经常会用到倒计时这个功能,而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哈哈~

更多相关文章

  1. 浅谈Java中Collections.sort对List排序的两种方法
  2. Python list sort方法的具体使用
  3. python list.sort()根据多个关键字排序的方法实现
  4. android上一些方法的区别和用法的注意事项
  5. android实现字体闪烁动画的方法
  6. Android(安卓)Wifi模块分析(三)
  7. Android中dispatchDraw分析
  8. Android四大基本组件介绍与生命周期
  9. Android(安卓)MediaPlayer 常用方法介绍

随机推荐

  1. Android(安卓)Studio 错误: 非法字符: '
  2. Android 动态加载(三) - 类的加载流程源码
  3. Android Layout属性笔记
  4. 《Android 从初学者入门到成为高手 视频
  5. Android使用fitsSystemWindows属性实现–
  6. Android圆形水波纹按钮的实现(Ripple)
  7. Android技能树 — 排序算法基础小结
  8. android 写 xml时,加layout与不加的区别(如
  9. android:RecyclerView局部刷新那点事~
  10. Android开发工具——ADB(Android(安卓)De