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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 15:09:02