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

ViewPager2搭配ExoPlayer快速滑动切换视频时音轨混音问题求解

问题核心原因

  • ViewPager2默认离屏缓存(offscreenPageLimit)为1,会保留当前页左右各1个Fragment实例,快速滑动时旧Fragment未被销毁,ExoPlayer仍在后台运行
  • 现有代码暂停、释放逻辑存在空安全缺陷,未覆盖Fragment不可见但未触发onPause的场景
  • prepareMedia方法直接设置playWhenReady = true,Fragment只要创建完成就自动播放,不判断是否对用户可见
  • 现有prepareMedia方法缺失MediaItem设置、prepare调用逻辑,播放器初始化逻辑不完整

修复步骤

1. 给Fragment增加可见性判断,只有可见时才播放

给StoryViewFragment新增可见状态标记,统一管理播放器状态:

// 新增变量标记当前Fragment是否对用户可见
private var isFragmentVisible = false

override fun setUserVisibleHint(isVisibleToUser: Boolean) {
    super.setUserVisibleHint(isVisibleToUser)
    isFragmentVisible = isVisibleToUser
    handlePlayerState()
}

override fun onHiddenChanged(hidden: Boolean) {
    super.onHiddenChanged(hidden)
    isFragmentVisible = !hidden
    handlePlayerState()
}

// 统一处理播放器状态
private fun handlePlayerState() {
    if (isFragmentVisible && isResumed) {
        restartVideo()
    } else {
        pauseVideo()
        // 滑动频次高的场景可直接释放播放器节省内存
        // releasePlayer()
    }
}

// 修改生命周期回调,配合可见性判断
override fun onResume() {
    super.onResume()
    handlePlayerState()
}

override fun onPause() {
    super.onPause()
    handlePlayerState()
}

2. 修复现有代码空指针和逻辑缺陷

// 暂停逻辑做空判断
private fun pauseVideo() {
    simplePlayer?.playWhenReady = false
}

// 释放逻辑补全置空操作
private fun releasePlayer() {
    simplePlayer?.let {
        it.stop()
        it.release()
        simplePlayer = null
    }
}

// 补全prepareMedia的播放器初始化逻辑,移除自动播放配置
private fun prepareMedia(datamodel: Gettrendingvideos.Data.Postlist?) {
    val uri = Uri.parse(datamodel?.video_url)
    simplePlayer?.repeatMode = Player.REPEAT_MODE_ONE
    // 删除原有自动播放配置 simplePlayer.playWhenReady = true
    // 新增媒体源设置逻辑
    val mediaItem = MediaItem.fromUri(uri)
    simplePlayer?.setMediaItem(mediaItem)
    simplePlayer?.prepare()
    // 原有其他业务逻辑保留
    // ...
    simplePlayer?.addListener(playerCallback)
    toPlayVideoPosition = -1
    // 最后统一判断状态决定是否播放
    handlePlayerState()
}

3. 可选优化:在宿主层统一管理播放状态

如果不想每个Fragment独立管理状态,可在ViewPager2的宿主中监听页面切换,统一控制播放:

// 宿主初始化ViewPager2时添加监听
viewPager2.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
    override fun onPageSelected(position: Int) {
        super.onPageSelected(position)
        supportFragmentManager.fragments.forEach { fragment ->
            if (fragment is StoryViewFragment) {
                // 匹配Fragment对应的位置,只有当前选中页播放,其他全部暂停
                val fragmentPosition = // 可通过给Fragment新增参数存储对应position获取
                if (fragmentPosition == position) {
                    fragment.restartVideo()
                } else {
                    fragment.pauseVideo()
                }
            }
        }
    }
})

4. 补充View销毁时的释放逻辑

在View销毁时就释放播放器,不要等到Fragment全销毁再处理:

override fun onDestroyView() {
    releasePlayer()
    super.onDestroyView()
}

内容的提问来源于stack exchange,提问作者Amit Chavan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 01:39:01