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
相关产品推荐
相关产品推荐

