Android Studio中使用MediaPlayer时音频无法停止的问题
问题分析与解决方案
问题根源
- 实例不唯一:每次点击按钮都会创建新的
AudioClass实例,Start按钮启动的是A实例的MediaPlayer,Stop按钮操作的是B实例的MediaPlayer,自然无法停止正在播放的音频。 - 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
相关产品推荐
相关产品推荐

