Android Studio Kotlin多Activity连续倒计时定时器实现咨询
多Activity共享倒计时实现方案(Kotlin)
针对答题类App多Activity共享倒计时的需求,这里提供两种适合新手的实现方式,从简单到进阶:
方式一:ViewModel + LiveData(基础版,适合前台计时)
利用AndroidViewModel的应用级生命周期特性,让多个Activity共享同一个倒计时状态,实现无缝切换时的计时延续。
1. 创建倒计时ViewModel
这个ViewModel持有倒计时逻辑和状态,所有Activity共用同一个实例:
import android.app.Application import android.os.CountDownTimer import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.MutableLiveData class CountdownViewModel(application: Application) : AndroidViewModel(application) { // 剩余时间(秒),LiveData自动通知UI更新 val remainingTime = MutableLiveData<Long>() private var countDownTimer: CountDownTimer? = null // 可自定义总时长,比如60秒答题时间 private val totalTime = 60000L private val updateInterval = 1000L // 每秒刷新一次 // 启动倒计时(仅需调用一次) fun startCountdown() { countDownTimer?.cancel() // 避免重复启动 countDownTimer = object : CountDownTimer(totalTime, updateInterval) { override fun onTick(millisUntilFinished: Long) { remainingTime.value = millisUntilFinished / 1000 } override fun onFinish() { remainingTime.value = 0 // 倒计时结束的统一逻辑,比如跳转到结果页 } }.start() } // 暂停倒计时(可选) fun pauseCountdown() { countDownTimer?.cancel() } // ViewModel销毁时清理定时器 override fun onCleared() { super.onCleared() countDownTimer?.cancel() } }
2. 在Activity中使用ViewModel
每个答题Activity都通过AndroidViewModelFactory获取同一个ViewModel实例,观察时间变化并更新UI:
示例:第一题Activity
import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import androidx.lifecycle.ViewModelProvider import kotlinx.android.synthetic.main.activity_question1.* class Question1Activity : AppCompatActivity() { private lateinit var viewModel: CountdownViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_question1) // 获取应用级ViewModel,确保多Activity共享实例 viewModel = ViewModelProvider( this, ViewModelProvider.AndroidViewModelFactory(application) )[CountdownViewModel::class.java] // 观察时间变化,实时更新UI viewModel.remainingTime.observe(this) { seconds -> tv_countdown.text = "剩余时间:$seconds 秒" if (seconds == 0L) { // 当前页面的结束逻辑,比如禁止答题 } } // 仅在首次进入时启动倒计时 if (viewModel.remainingTime.value == null) { viewModel.startCountdown() } // 跳转到下一题 btn_next.setOnClickListener { startActivity(Intent(this, Question2Activity::class.java)) } } }
示例:第二题Activity
和第一题逻辑几乎一致,仅需重复获取ViewModel并观察即可:
import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import androidx.lifecycle.ViewModelProvider import kotlinx.android.synthetic.main.activity_question2.* class Question2Activity : AppCompatActivity() { private lateinit var viewModel: CountdownViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_question2) viewModel = ViewModelProvider( this, ViewModelProvider.AndroidViewModelFactory(application) )[CountdownViewModel::class.java] viewModel.remainingTime.observe(this) { seconds -> tv_countdown.text = "剩余时间:$seconds 秒" if (seconds == 0L) { // 当前页面的结束逻辑 } } btn_next.setOnClickListener { startActivity(Intent(this, ResultActivity::class.java)) } } }
方式二:Foreground Service(进阶版,支持后台计时)
如果需要App退到后台时仍继续计时,就需要用前台Service实现(Android要求后台长时间运行的Service必须设为前台,避免被系统杀死)。
1. 创建倒计时Service
import android.app.Notification import android.app.NotificationChannel import android.app.NotificationManager import android.app.Service import android.content.Intent import android.os.Binder import android.os.Build import android.os.IBinder import android.os.CountDownTimer import androidx.core.app.NotificationCompat class CountdownService : Service() { private val binder = LocalBinder() private var countDownTimer: CountDownTimer? = null var remainingTime = 60L // 总时长60秒 private val channelId = "CountdownChannel" // 用于Activity绑定Service的Binder inner class LocalBinder : Binder() { fun getService(): CountdownService = this@CountdownService } override fun onCreate() { super.onCreate() createNotificationChannel() // 启动前台Service,显示通知 startForeground(1, getNotification()) startTimer() } private fun startTimer() { countDownTimer = object : CountDownTimer(remainingTime * 1000, 1000) { override fun onTick(millisUntilFinished: Long) { remainingTime = millisUntilFinished / 1000 // 发送广播通知所有Activity更新UI val updateIntent = Intent("COUNT_DOWN_UPDATE") updateIntent.putExtra("remaining_time", remainingTime) sendBroadcast(updateIntent) } override fun onFinish() { remainingTime = 0 val finishIntent = Intent("COUNT_DOWN_FINISH") sendBroadcast(finishIntent) stopForeground(STOP_FOREGROUND_REMOVE) stopSelf() } }.start() } // 创建通知渠道(Android 8.0+必需) private fun createNotificationChannel() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val channel = NotificationChannel( channelId, "答题倒计时", NotificationManager.IMPORTANCE_LOW ) val manager = getSystemService(NotificationManager::class.java) manager.createNotificationChannel(channel) } } // 构建前台通知 private fun getNotification(): Notification { return NotificationCompat.Builder(this, channelId) .setContentTitle("答题进行中") .setContentText("剩余${remainingTime}秒") .setSmallIcon(R.drawable.ic_timer) // 替换成你的图标 .build() } override fun onBind(intent: Intent): IBinder = binder override fun onDestroy() { super.onDestroy() countDownTimer?.cancel() } }
2. 在Manifest中注册Service
<service android:name=".CountdownService" android:foregroundServiceType="dataSync"/>
3. 在Activity中绑定Service并接收广播
每个Activity通过广播接收倒计时更新,保持UI同步:
import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.content.IntentFilter import kotlinx.android.synthetic.main.activity_question1.* class Question1Activity : AppCompatActivity() { private var countdownService: CountdownService? = null private var isBound = false // 广播接收器,接收倒计时更新 private val countdownReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { when (intent?.action) { "COUNT_DOWN_UPDATE" -> { val time = intent.getLongExtra("remaining_time", 0) tv_countdown.text = "剩余时间:$time 秒" } "COUNT_DOWN_FINISH" -> { tv_countdown.text = "时间到!" // 处理结束逻辑 } } } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_question1) // 注册广播接收器 val filter = IntentFilter().apply { addAction("COUNT_DOWN_UPDATE") addAction("COUNT_DOWN_FINISH") } registerReceiver(countdownReceiver, filter) // 启动并绑定Service val serviceIntent = Intent(this, CountdownService::class.java) startService(serviceIntent) bindService(serviceIntent, serviceConnection, Context.BIND_AUTO_CREATE) btn_next.setOnClickListener { startActivity(Intent(this, Question2Activity::class.java)) } } // Service连接回调 private val serviceConnection = object : android.content.ServiceConnection { override fun onServiceConnected(className: android.content.ComponentName, service: IBinder) { val binder = service as CountdownService.LocalBinder countdownService = binder.getService() isBound = true // 初始化UI显示 tv_countdown.text = "剩余时间:${countdownService?.remainingTime} 秒" } override fun onServiceDisconnected(arg0: android.content.ComponentName) { isBound = false } } // 销毁时解绑Service和注销广播 override fun onDestroy() { super.onDestroy() unregisterReceiver(countdownReceiver) if (isBound) { unbindService(serviceConnection) isBound = false } } }
新手注意事项
- 基础版(ViewModel)适合仅在前台答题的场景,实现简单,无需处理权限。
- 进阶版(Service)需要处理前台Service权限,Android 13+需申请
POST_NOTIFICATIONS权限。 - 布局中需添加用于显示倒计时的
TextView(示例中的tv_countdown)。
内容的提问来源于stack exchange,提问作者Callee20
相关产品推荐
相关产品推荐

