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

Android Studio中使用MediaPlayer时音频无法停止的问题

问题分析与解决方案

问题根源

  1. 实例不唯一:每次点击按钮都会创建新的AudioClass实例,Start按钮启动的是A实例的MediaPlayer,Stop按钮操作的是B实例的MediaPlayer,自然无法停止正在播放的音频。
  2. MediaPlayer状态错误:重复调用prepare()会导致MediaPlayer状态混乱,stop()只能在Started、Paused、PlaybackCompleted状态下调用,错误状态下调用会抛出-38错误(表示操作不符合当前状态)。

修复步骤

1. 将AudioClass改为单例,实现全局唯一实例

确保所有操作都针对同一个MediaPlayer:

// 全局单例的音频管理类
class AudioClass private constructor(context: Context) {
    private val mediaPlayer: MediaPlayer = MediaPlayer().apply {
        setDataSource(context, Uri.parse(soundEffectUri))
        setAudioStreamType(AudioManager.STREAM_ALARM)
        isLooping = true
        prepareAsync() // 用异步prepare避免阻塞主线程
        setOnPreparedListener {
            // 准备完成后的回调,可选
        }
    }

    // 播放逻辑:先检查状态,避免重复prepare/start
    fun play() {
        if (!mediaPlayer.isPlaying) {
            when (mediaPlayer.state) {
                MediaPlayer.STATE_PREPARED -> mediaPlayer.start()
                MediaPlayer.STATE_STOPPED -> {
                    mediaPlayer.reset()
                    mediaPlayer.setDataSource(mediaPlayer.context, Uri.parse(soundEffectUri))
                    mediaPlayer.prepareAsync()
                    mediaPlayer.setOnPreparedListener { it.start() }
                }
            }
        }
    }

    // 停止逻辑:检查当前状态,仅在可停止状态执行
    fun stop() {
        if (mediaPlayer.isPlaying || mediaPlayer.state == MediaPlayer.STATE_PAUSED) {
            mediaPlayer.stop()
        }
    }

    // 释放资源,建议在应用退出时调用
    fun release() {
        if (mediaPlayer.isPlaying) mediaPlayer.stop()
        mediaPlayer.release()
    }

    // 单例实现
    companion object {
        @Volatile
        private var instance: AudioClass? = null

        fun getInstance(context: Context): AudioClass {
            return instance ?: synchronized(this) {
                instance ?: AudioClass(context.applicationContext).also { instance = it }
            }
        }
    }
}

2. 修改Composable中的按钮点击逻辑

使用单例实例,而非每次创建新对象:

// Sound Effect Uri
val soundEffectUri = "android.resource://com.example.audiotest/raw/sound_effect"

// App Layout
Column(
    modifier = Modifier.fillMaxSize(),
    verticalArrangement = Arrangement.Center,
    horizontalAlignment = Alignment.CenterHorizontally
) {
    // 获取全局音频实例
    val audioManager = AudioClass.getInstance(LocalContext.current)

    // 播放按钮
    Button(onClick = { audioManager.play() }) {
        Text(text = "Start")
    }

    // 停止按钮
    Button(onClick = { audioManager.stop() }) {
        Text(text = "Stop")
    }
}

3. 关键注意事项

  • 用prepareAsync()替代同步prepare(),避免阻塞UI线程;
  • 操作MediaPlayer前必须检查当前状态,严格遵循MediaPlayer的状态机规则;
  • 在应用退出时(比如Activity的onDestroy())调用audioManager.release()释放资源,避免内存泄漏。

错误码解释

  • stop called in state 2:状态2对应MediaPlayer.STATE_PREPARED,此时MediaPlayer还未启动,调用stop()不符合状态规则;
  • error (-38, 0):表示操作请求与当前MediaPlayer状态不匹配。

内容的提问来源于stack exchange,提问作者Bob Rasner

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 23:51:10