如何在Fragment中调用Music类stop方法在onPause时停止音频播放
问题核心原因
- 你每次点击音频按钮都会新建独立的
Music实例,实例之间没有共享状态,自然无法在Fragment中调用同一个实例的stop方法 - 已定义的
MusicPlayerListener回调没有正确触发,当前Fragment的mMediaPlayer变量根本收不到正在播放的MediaPlayer实例,导致onPause里的停止逻辑完全不生效 Music类自带的stop()方法逻辑错误,内部调用play()会重复创建MediaPlayer,根本无法正常停止当前播放的音频
修改步骤
1. 修复Music类的stop方法
class Music(var button: Button, val context: Context, val resources: Resources, val Id: Int, var buttonArray: MutableList<Button>, val mpArray: MutableList<MediaPlayer>) { // 原有play方法不变,仅修改stop方法逻辑 fun stop() { // 直接遍历所有缓存的MediaPlayer停止并释放 mpArray.forEach { mp -> if (mp.isPlaying) { mp.stop() mp.reset() mp.release() } } mpArray.clear() buttonArray.clear() } }
2. 修改Adapter逻辑,对外暴露停止方法、正确触发回调
class Adapters(private val cards: List<Card>, val resources: Resources, val context: Context, private val musicPlayerListener: MusicPlayerListener) : RecyclerView.Adapter<RecyclerView.ViewHolder>() { private var buttonArray = mutableListOf<Button>() private var mpArray = mutableListOf<MediaPlayer>() // 对外暴露停止所有音频的公共方法 fun stopAllAudio() { mpArray.forEach { mp -> if (mp.isPlaying) { mp.stop() mp.reset() mp.release() } } mpArray.clear() buttonArray.clear() } override fun getItemViewType(position: Int): Int = when(cards[position]) { is Card.AudioButton -> 11 else -> throw IllegalArgumentException("Error") } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { return object : RecyclerView.ViewHolder( when(viewType) { 11 -> LayoutInflater.from(parent.context).inflate(R.layout.in_lesson_audio_button, parent, false) else -> throw IllegalArgumentException("Error") }) { } } override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { when(val card = cards[position]) { is Card.AudioButton -> { val button = holder.itemView.findViewById<Button>(R.id.in_lesson_audio_button) button.setOnClickListener { val audioId = getAudioId(card.audioButton) val music = Music(button, context, resources, audioId, buttonArray, mpArray) // 播放后拿到MediaPlayer实例,触发回调给Fragment val mediaPlayer = music.play() musicPlayerListener.onMusicPlay(mediaPlayer) } } } } override fun getItemCount() = cards.size private fun getAudioId(audioElement: String): Int = resources.getIdentifier(audioElement, "raw", context.packageName) }
3. 修改Fragment逻辑,保存Adapter实例、调用停止方法
class Fragment : Fragment(), MusicPlayerListener { private val args: DetailsFragmentArgs by navArgs() var mMediaPlayer: MediaPlayer? = null // 保存Adapter实例,后续调用停止方法 private var audioAdapter: Adapters? = null override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?, ): View? { val binding = inflater.inflate(R.layout.fragment, container, false) val recyclerView = binding.findViewById<RecyclerView>(R.id.list_view) recyclerView.layoutManager = LinearLayoutManager(context) // 初始化Adapter时传入this作为回调监听,同时保存实例 audioAdapter = Adapters(parseLesson(resources,R.xml.text), resources, requireContext(), this) recyclerView.adapter = audioAdapter return binding } override fun onPause() { super.onPause() // 调用Adapter暴露的停止方法,统一停止所有正在播放的音频 audioAdapter?.stopAllAudio() // 补充释放当前持有的MediaPlayer mMediaPlayer?.let { if (it.isPlaying) { it.stop() it.reset() it.release() } } mMediaPlayer = null } override fun onMusicPlay(mediaPlayer: MediaPlayer) { // 每次播放新音频前先释放之前的实例,避免多个音频同时播放 mMediaPlayer?.let { if (it.isPlaying) { it.stop() it.reset() it.release() } } mMediaPlayer = mediaPlayer } }
优化建议
- 可将
buttonArray和mpArray从Music类构造参数中抽离,单独封装为单例的音频播放管理类,无需每次点击都新建Music实例,减少资源浪费 - 给MediaPlayer的操作逻辑添加异常捕获,避免出现
IllegalStateException崩溃 - 页面
onDestroy时也要补充释放所有音频资源,避免内存泄漏
内容的提问来源于stack exchange,提问作者George Kusenkov
相关产品推荐
相关产品推荐

