You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android实现后台运行倒计时:关闭应用仍可正常计时的解决方案

实现Android应用关闭后仍持续倒计时的方案

现有代码中的CountDownTimer直接绑定MainActivity生命周期,仅在Activity存活时运行,一旦Activity销毁、应用进程被回收或用户手动关闭应用,计时器就会被销毁,导致计时重置。

最优实现方案:基于持久化时间戳的无保活方案

这个方案不需要后台保活,适配所有Android版本,稳定性远高于后台服务方案,核心逻辑是不依赖内存实时计时,而是记录倒计时的结束时间,每次进入页面时通过当前时间和结束时间的差值计算剩余计时。

实现步骤:

  • 使用SharedPreferences持久化存储倒计时结束时间戳
  • 点击启动倒计时时,计算结束时间(当前时间戳 + 倒计时总时长)写入持久化存储,同时启动页面内的计时器更新UI
  • Activity初始化时,读取存储的结束时间,和当前时间对比自动恢复计时状态
  • 若需要倒计时结束时即使应用未打开也能收到通知,可搭配AlarmManager设置定时通知

修改后的完整代码:

package com.mycompany.myapp;

import android.app.Activity;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.widget.Button;
import android.widget.TextView;
import android.view.View;

public class MainActivity extends Activity {
    Button btnCountdown;
    TextView tvCountdown;
    CountDownTimer countDownTimer;
    SharedPreferences sp;
    // 倒计时总时长:30秒
    private static final long TOTAL_TIME = 30 * 1000L;
    private static final String SP_NAME = "countdown_sp";
    private static final String KEY_END_TIME = "end_time";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        sp = getSharedPreferences(SP_NAME, MODE_PRIVATE);
        btnCountdown = findViewById(R.id.btnCountdown);
        tvCountdown = findViewById(R.id.tvCountdown);

        // 页面启动时先恢复计时状态
        restoreCountdownState();

        btnCountdown.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                long endTime = System.currentTimeMillis() + TOTAL_TIME;
                // 存储结束时间戳
                sp.edit().putLong(KEY_END_TIME, endTime).apply();
                startCountdown(endTime - System.currentTimeMillis());
                btnCountdown.setEnabled(false);
            }
        });
    }

    /**
     * 恢复倒计时状态
     */
    private void restoreCountdownState() {
        long endTime = sp.getLong(KEY_END_TIME, 0);
        long currentTime = System.currentTimeMillis();
        if (endTime > currentTime) {
            // 倒计时还未结束,恢复计时
            startCountdown(endTime - currentTime);
            btnCountdown.setEnabled(false);
        } else {
            // 倒计时已结束或未启动
            tvCountdown.setText("Fin");
            btnCountdown.setEnabled(true);
            // 清理过期的存储数据
            sp.edit().remove(KEY_END_TIME).apply();
        }
    }

    /**
     * 启动页面计时器更新UI
     */
    private void startCountdown(long remainingTime) {
        // 先取消已存在的计时器避免重复
        if (countDownTimer != null) {
            countDownTimer.cancel();
        }
        countDownTimer = new CountDownTimer(remainingTime,1000) {
            @Override
            public void onTick(long millisUntilFinished) {
                long second = (millisUntilFinished / 1000) % 60;
                long minutes = (millisUntilFinished / (1000*60)) % 60;
                tvCountdown.setText(String.format("%02d:%02d", minutes, second));
            }
            @Override
            public void onFinish() {
                tvCountdown.setText("Fin");
                btnCountdown.setEnabled(true);
                // 清理存储的结束时间
                sp.edit().remove(KEY_END_TIME).apply();
            }
        }.start();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 页面销毁时取消当前计时器,避免内存泄漏,下次进入会自动恢复
        if (countDownTimer != null) {
            countDownTimer.cancel();
        }
    }
}

可选扩展:添加离线结束通知

如果需要倒计时结束时即使应用未打开也能给用户发通知,可以在启动倒计时的时候同时调用AlarmManager设置定时触发的PendingIntent,发送系统通知即可,不需要后台服务持续运行。

内容的提问来源于stack exchange,提问作者Abolfazl SM

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.30 21:24:03