ViewPager的PagerAdapter重复调用导致Exoplayer音频异常播放问题求助
核心问题根因
- ExoPlayer实例管理错误:你将
ImageSlideAdapter中的player声明为静态变量,导致所有视频项共用同一个播放器实例,操作时互相干扰,这是多音频同时播放的核心原因。 - ViewPager预加载逻辑冲突:
setOffscreenPageLimit(0)是无效配置,Android ViewPager的最小预加载页数为1,前后相邻页会提前执行instantiateItem初始化,视频页预加载时调用player.prepare()后没有主动暂停,就会触发后台播放。 - 状态判断逻辑错误:
onPageSelected里的播放/暂停判断逻辑混乱,currentPage == position的判断永远为真,导致判断条件完全失效。 - 资源释放时机错误:
OnAttachStateChangeListener里的暂停/释放逻辑完全错误,页面detach时应该直接释放当前页的播放器,而不是根据全局页面判断。 - 播放器状态未统一管理:没有统一的逻辑控制只有当前选中的视频页可以播放,其余页面都要暂停并释放资源。
具体修复步骤
1. 修改ImageSlideAdapter,移除静态player,统一管理播放器实例
首先移除全局静态player变量,新增SparseArray存储每个视频位置对应的独立ExoPlayer实例:
// ImageSlideAdapter类顶部新增成员变量 private SparseArray<ExoPlayer> playerMap = new SparseArray<>();
修改instantiateItem中的视频初始化逻辑,创建player后存入map,prepare后主动暂停,等待页面选中再播放,同时移除原有无效的OnAttachStateChangeListener逻辑:
if (currentFile.getAbsolutePath().endsWith(".mp4")) { statusImageView.setVisibility(View.GONE); playerView.setVisibility(View.VISIBLE); // 每个视频项创建独立播放器实例 ExoPlayer player = new ExoPlayer.Builder(context).build(); // 存入map统一管理 playerMap.put(position, player); MediaItem mediaItem = MediaItem.fromUri(filePath); player.addMediaItem(mediaItem); playerView.setPlayer(player); player.prepare(); // 初始化后主动暂停,避免预加载时自动播放 player.setPlayWhenReady(false); playerView.setBackgroundColor(context.getResources().getColor(android.R.color.black)); }
修改destroyItem方法,页面销毁时释放对应播放器资源:
@Override public void destroyItem(@NonNull @NotNull ViewGroup container, int position, @NonNull @NotNull Object object) { // 销毁页面之前先释放对应位置的播放器 ExoPlayer player = playerMap.get(position); if (player != null) { player.release(); playerMap.remove(position); } container.removeView((ConstraintLayout) object); }
2. 重构StatusViewer的onPageSelected逻辑,统一控制播放状态
@Override public void onPageSelected(int position) { currentPage = position; File currentFile = modelFeedArrayList.get(position); // 先暂停所有已创建的播放器,避免多实例同时播放 for(int i = 0; i < imageSlideAdapter.playerMap.size(); i++) { ExoPlayer player = imageSlideAdapter.playerMap.valueAt(i); if (player != null) { player.setPlayWhenReady(false); } } // 仅当前选中页为视频时才触发播放 if (currentFile.getAbsolutePath().endsWith(".mp4")) { ExoPlayer currentPlayer = imageSlideAdapter.playerMap.get(position); if (currentPlayer != null) { currentPlayer.setPlayWhenReady(true); } } }
3. 处理点击事件冲突
给TouchImageView和PlayerView的点击事件增加拦截,点击时仅处理当前选中页的逻辑,不要触发其他页面的播放:
- TouchImageView点击时不要修改播放器状态,统一走
onPageSelected的控制逻辑 - PlayerView点击时仅控制当前播放器的暂停/播放,不要影响其他实例
替代方案推荐
如果需要更平滑的体验,可替换为ViewPager2实现,ViewPager2基于RecyclerView实现,默认支持懒加载,不会预加载不必要的页面,可大幅降低播放器状态管理的复杂度。
内容的提问来源于stack exchange,提问作者Devarshi Rawal
相关产品推荐
相关产品推荐

