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

如何在UI层响应ViewModel中CountDownTimer的onTick事件并播放声音?

解决方案:在UI层响应ViewModel的倒计时回调播放音频

由于ViewModel不应持有Context或处理UI相关操作(比如音频播放),推荐以下几种可行方案:

方案一:观察ViewModel中的remainingTime状态

ViewModel已经在onTick中更新了_remainingTime,UI层可以直接观察这个状态变量,每次值变化时触发音频播放:

ViewModel代码(无需修改原有逻辑)

fun startTimer() {
    stopAllTimers()
    _isRunning.value = true
    countDownTimer = object : CountDownTimer(duration, INTERVAL) {
        override fun onTick(millisUntilFinished: Long) {
            _remainingTime.value = millisUntilFinished / 1000
        }
        override fun onFinish() {}
    }.start()
}

UI层(Activity/Fragment示例)

在UI层监听remainingTime的变化,触发音频播放:

// 以Activity为例,在onCreate中设置观察
viewModel.remainingTime.observe(this) {
    playTickSound()
}

// 音频播放实现示例
private fun playTickSound() {
    val mediaPlayer = MediaPlayer.create(this, R.raw.tick_sound)
    mediaPlayer.start()
    mediaPlayer.setOnCompletionListener { it.release() }
}

方案二:通过回调接口传递onTick事件

定义回调接口,ViewModel持有接口的弱引用(避免内存泄漏),UI层实现接口并传入ViewModel,在onTick中触发回调:

定义回调接口

interface TimerTickListener {
    fun onTimerTick()
}

ViewModel修改

// 使用WeakReference防止内存泄漏
private var tickListener: WeakReference<TimerTickListener>? = null

fun setTimerTickListener(listener: TimerTickListener?) {
    tickListener = listener?.let { WeakReference(it) }
}

fun startTimer() {
    stopAllTimers()
    _isRunning.value = true
    countDownTimer = object : CountDownTimer(duration, INTERVAL) {
        override fun onTick(millisUntilFinished: Long) {
            _remainingTime.value = millisUntilFinished / 1000
            // 调用UI层的回调方法
            tickListener?.get()?.onTimerTick()
        }
        override fun onFinish() {}
    }.start()
}

UI层实现接口

class YourActivity : AppCompatActivity(), TimerTickListener {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        viewModel.setTimerTickListener(this)
    }

    override fun onTimerTick() {
        playTickSound()
    }

    private fun playTickSound() {
        // 音频播放逻辑
    }

    override fun onDestroy() {
        super.onDestroy()
        // 清空引用,避免泄漏
        viewModel.setTimerTickListener(null)
    }
}

方案三:使用SharedFlow发送事件

如果使用Kotlin协程,可以在ViewModel中用SharedFlow发送tick事件,UI层收集事件并处理:

ViewModel代码

private val _tickEvents = MutableSharedFlow<Unit>()
val tickEvents = _tickEvents.asSharedFlow()

fun startTimer() {
    stopAllTimers()
    _isRunning.value = true
    countDownTimer = object : CountDownTimer(duration, INTERVAL) {
        override fun onTick(millisUntilFinished: Long) {
            _remainingTime.value = millisUntilFinished / 1000
            // 发送tick事件
            viewModelScope.launch {
                _tickEvents.emit(Unit)
            }
        }
        override fun onFinish() {}
    }.start()
}

UI层收集事件

lifecycleScope.launch {
    repeatOnLifecycle(Lifecycle.State.STARTED) {
        viewModel.tickEvents.collect {
            playTickSound()
        }
    }
}

内容的提问来源于stack exchange,提问作者jaskaran singh

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 15:07:27