解决Android中CountDownTimer倒计时cancel方法无效的问题

本文介绍了解决Android CountDownTimer在5.0以下版本无法通过cancel方法取消的问题。提供了一个修改后的CountDownTimer类,并展示了如何在项目中实现自定义的倒计时功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、概述

在项目中使用Android自带的CountDownTimer倒计时,在调用cancel方法时发现无法取消,onTick方法依然执行,百度一番,据说5.0以上才能正常cancel,5.0以下是没办法cancel的。

解决办法:在项目里使用5.0的CountDownTimer,把5.0里面的这个类拷贝到项目中,在代码里使用这个新拷贝的类,就可以解决问题了。

定义倒计时类

public class CountDown extends CountDownTimer {

        public CountDown(long millisInFuture, long countDownInterval) {
            super(millisInFuture, countDownInterval);
        }

        @Override
        public void onFinish() {
            updateDialog();
        }

        @Override
        public void onTick(long millisUntilFinished) {
            LogUtil.e("剩余 " + millisUntilFinished / 1000 + "秒");
        }

    }

调用倒计时

myCount = new CountDown(VALIDITY_TIME, 1000);
        myCount.start();

从5.0系统拷贝过来新的CountDownTimer

package com.aldx.hccraftsman.utils;

import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;

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

    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.
     *
     * Do not call it from inside CountDownTimer threads
     */
    public final void cancel() {
        mHandler.removeMessages(MSG);
        mCancelled = true;
    }

    /**
     * Start the countdown.
     */
    public synchronized final CountDownTimer start() {
        if (mMillisInFuture <= 0) {
            onFinish();
            return this;
        }
        mStopTimeInFuture = SystemClock.elapsedRealtime() + mMillisInFuture;
        mHandler.sendMessage(mHandler.obtainMessage(MSG));
        mCancelled = false;
        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) {
                final long millisLeft = mStopTimeInFuture - SystemClock.elapsedRealtime();

                if (millisLeft <= 0) {
                    onFinish();
                } else if (millisLeft < mCountdownInterval) {
                    // no tick, just delay until done
                    sendMessageDelayed(obtainMessage(MSG), millisLeft);
                } else {
                    long lastTickStart = SystemClock.elapsedRealtime();
                    onTick(millisLeft);

                    // take into account user's onTick taking time to execute
                    long delay = lastTickStart + mCountdownInterval - SystemClock.elapsedRealtime();

                    // special case: user's onTick took more than interval to
                    // complete, skip to next interval
                    while (delay < 0) delay += mCountdownInterval;

                    if (!mCancelled) {
                        sendMessageDelayed(obtainMessage(MSG), delay);
                    }
                }
            }
        }
    };
}

另外为防止程序奔溃,在activity销毁时加上这段代码:

@Override
    protected void onDestroy() {
        super.onDestroy();
        if(myCount!=null){
            myCount.cancel();
        }
    }
<think>我们正在解决Android中Timer启动后异常退出的问题。根据提供的引用,我们注意到有几个关键点:1.引用[1]中提到了两个异常:"Timerwascanceled"和"TimerTaskisscheduledalready"。这表明在Timer已经被取消后再次使用,或者尝试重复调度同一个TimerTask会导致异常。2.引用[2]提供了一种替代方案:使用CountDownTimer来实现定时任务,这可能比Timer更稳定。3.引用[3]则展示了一个使用Service来管理超时的例子,其中使用了Timer,但需要注意在Service中正确管理Timer的生命周期。因此,我们可以从以下几个方面来避免Timer异常退出:-确保Timer和TimerTask的正确使用(例如,不要重复调度同一个TimerTask,不要在被取消的Timer上再次调度)。-考虑使用替代方案(如CountDownTimer)来简化定时任务。-在Service中管理Timer时,注意在Service销毁时取消Timer。下面我们将详细说明解决方案。</think>### Android Timer 异常退出解决方案 在 Android 中使用 `Timer` 和 `TimerTask` 时出现异常退出(如 "Timer was canceled" 或 "TimerTask is scheduled already"),通常由生命周期管理不当引起。以下是系统级解决方案: #### 核心问题分析 1. **重复调度问题** 同一 `TimerTask` 被多次调度会触发 `IllegalStateException`[^1] 2. **生命周期不匹配** `Timer` 未随组件(Activity/Service)销毁而终止,导致后台线程持有无效引用[^3] 3. **线程管理缺陷** `Timer` 创建的线程未关联组件生命周期,组件销毁后线程仍运行 --- #### 解决方案代码实现 ```java // 在Activity/Service中声明 private Timer mTimer; private TimerTask mTask; @Override protected void onStart() { super.onStart(); startTimer(); } private void startTimer() { // 确保旧实例被清理 cancelTimer(); mTimer = new Timer(); mTask = new TimerTask() { @Override public void run() { // 添加空指针检查 if (!isFinishing()) { runOnUiThread(() -> { // 执行定时操作 }); } } }; // 设置延迟执行 mTimer.schedule(mTask, 1000, 5000); } @Override protected void onStop() { super.onStop(); cancelTimer(); } private void cancelTimer() { if (mTimer != null) { mTimer.cancel(); // 终止计时器 mTimer.purge(); // 清除任务队列 mTimer = null; // 释放引用 } if (mTask != null) { mTask.cancel(); // 标记任务为取消状态 mTask = null; } } ``` #### 关键优化点 1. **双保险终止机制** `timer.cancel()` + `timer.purge()` 确保彻底清除任务队列[^1] 2. **生命周期绑定** 在 `onStop()`/`onDestroy()` 中强制销毁 Timer 3. **空引用防护** 在执行线程任务前检查 `isFinishing()` 或 `isDestroyed()` 4. **资源释放** 显式置空 `mTimer` 和 `mTask` 防止内存泄漏 --- #### 替代方案推荐 ```java // 使用 CountDownTimer(自动关联主线程) new CountDownTimer(30000, 1000) { public void onTick(long millisUntilFinished) { // 更新UI } public void onFinish() { // 超时处理 } }.start(); ``` 优势:自动运行在UI线程,无需手动切换线程;内部封装生命周期更安全[^2] --- #### 服务(Service)场景优化 ```java public class TimeoutService extends Service { private Timer mTimer; @Override public void onDestroy() { cancelTimer(); super.onDestroy(); } private void cancelTimer() { // 同上实现 } } ``` **注意**:在 Service 中需额外处理 `START_STICKY` 重启时的 Timer 重建问题[^3] ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值