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

Kotlin中非阻塞循环实现:音频播放与倒计时同步问题求助

问题:Kotlin Android音频播放与倒计时非阻塞实现

我是Kotlin编程初学者,目前正在开发一款小型应用,需要实现两个功能:

  • 先播放一段音频2秒,接着播放另一段音频20秒;
  • 在音频播放期间从22倒计时至0。

单独的倒计时和单独的音频播放功能均正常,我编写的音频播放代码如下:

var player1 = MediaPlayer.create(this, com.example.dindon.R.raw.bell)
var player2 = MediaPlayer.create(this, com.example.dindon.R.raw.pink)
player1!!.setVolume(1F, 1F)
player1!!.start()
var timeNow = LocalDateTime.now()
while (LocalDateTime.now().minusSeconds(2) < timeNow) {}
player1!!.stop()
player1.prepareAsync();
player2!!.start()
timeNow = LocalDateTime.now()
while (LocalDateTime.now().minusSeconds(20) < timeNow) {}
player2!!.stop()
player2.prepareAsync();
player1!!.release()
player1 = null
player2!!.release()
player2 = null

这段代码中的两个while循环是为了保证音频播放指定时长,否则stop指令会紧随start执行,导致无法听到音频。但问题在于这两个循环会阻塞其他所有操作,启动后无法看到倒计时及应用内其他变化,甚至会阻止键盘监听器执行(我需要该监听器输入音频循环播放的次数)。我尝试过线程、Thread.sleep等方案,但仍然存在阻塞问题,每次执行while循环时应用看似冻结,实际上倒计时并未停止,只是循环结束后才显示0。

我希望实现音频完整播放且不干扰其他功能(尤其是倒计时)的非阻塞方案,特此求助。


解决方案:非阻塞式音频播放+倒计时实现

核心思路是利用MediaPlayer的完成监听回调控制音频切换,同时用Android原生工具或Kotlin协程实现倒计时,全程不阻塞主线程。

方案1:MediaPlayer监听+CountDownTimer(原生可靠)

1. 定义全局变量(避免局部变量被回收)

private var player1: MediaPlayer? = null
private var player2: MediaPlayer? = null
private var countDownTimer: CountDownTimer? = null

2. 初始化播放器并设置完成监听

// 初始化音频1,设置播放完成后触发音频2启动
player1 = MediaPlayer.create(this, R.raw.bell).apply {
    setVolume(1F, 1F)
    setOnCompletionListener { mp ->
        mp.stop()
        mp.release()
        player1 = null
        // 音频1结束后启动音频2
        player2?.start()
    }
}

// 初始化音频2,设置播放完成后清理资源
player2 = MediaPlayer.create(this, R.raw.pink).apply {
    setVolume(1F, 1F)
    setOnCompletionListener { mp ->
        mp.stop()
        mp.release()
        player2 = null
        // 音频2结束后停止倒计时(如果未完成)
        countDownTimer?.cancel()
    }
}

3. 启动整个流程(倒计时+音频播放)

fun startFullProcess() {
    // 启动22秒倒计时,每秒更新UI
    countDownTimer = object : CountDownTimer(22 * 1000, 1000) {
        override fun onTick(millisUntilFinished: Long) {
            // 更新倒计时显示,替换成你的UI控件
            val remainingSeconds = millisUntilFinished / 1000
            binding.tvCountDown.text = remainingSeconds.toString()
        }

        override fun onFinish() {
            binding.tvCountDown.text = "0"
            // 倒计时结束后停止音频2(如果还在播放)
            player2?.stop()
            player2?.release()
            player2 = null
        }
    }.start()

    // 启动第一段音频
    player1?.start()
}

4. 页面销毁时清理资源

override fun onDestroy() {
    super.onDestroy()
    player1?.release()
    player2?.release()
    countDownTimer?.cancel()
}

方案2:Kotlin协程实现(代码更简洁)

如果熟悉Kotlin协程,可用delay替代循环等待,同时在协程中处理音频切换:

private var player1: MediaPlayer? = null
private var player2: MediaPlayer? = null
private var processJob: Job? = null

fun startFullProcess() {
    processJob = lifecycleScope.launch {
        // 启动倒计时协程
        launch {
            var count = 22
            while (count >= 0) {
                binding.tvCountDown.text = count.toString()
                delay(1000)
                count--
            }
        }

        // 播放音频1并等待2秒
        player1 = MediaPlayer.create(this@MainActivity, R.raw.bell).apply {
            setVolume(1F, 1F)
            start()
        }
        delay(2000)
        player1?.stop()
        player1?.release()
        player1 = null

        // 播放音频2并等待20秒
        player2 = MediaPlayer.create(this@MainActivity, R.raw.pink).apply {
            setVolume(1F, 1F)
            start()
        }
        delay(20000)
        player2?.stop()
        player2?.release()
        player2 = null

        // 音频播放完成后取消倒计时(如果未结束)
        processJob?.cancel()
    }
}

override fun onDestroy() {
    super.onDestroy()
    player1?.release()
    player2?.release()
    processJob?.cancel()
}

关键注意事项

  • 绝对不要在主线程使用while循环或Thread.sleep,会直接阻塞UI更新
  • 务必在页面生命周期结束时释放MediaPlayer和倒计时资源,避免内存泄漏
  • 协程方案推荐使用lifecycleScope(需依赖AndroidX),确保协程随页面销毁自动取消

内容的提问来源于stack exchange,提问作者Andrea Carta

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 18:06:19