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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 07:37:57