SimpleExoPlayer的Player.EventListener失效,需实现播放时显示序号文本
我看了你的代码,问题主要出在Player的重复创建和错误的状态判断逻辑上,另外要实现“X of Y”的序号显示,得用正确的方式获取当前播放的媒体索引。下面一步步给你修正:
1. 修复Player监听器失效的问题
你在onCreate里创建了player并添加了eventListener,但在initializePlayer方法里又重新创建了一个新的player对象——这就导致之前添加的监听器被绑定到了旧的player实例上,新的player根本没有这个监听器,自然不会触发回调。
解决办法:把player的创建逻辑统一,不要重复创建。可以在initializePlayer里复用已经创建的player,或者把player的初始化移到initializePlayer里,再统一添加监听器。
2. 修正播放状态判断的错误
你在onPlayerStateChanged里用PlaybackStateCompat.ACTION_SKIP_TO_NEXT和ACTION_SKIP_TO_PREVIOUS来判断播放状态,这完全不对!这些常量是媒体会话的动作类型,而playbackState参数对应的是Player.STATE_*系列常量(比如Player.STATE_READY、Player.STATE_ENDED等)。要监听视频切换动作,应该用onPositionDiscontinuity回调,或者直接获取当前播放的媒体索引。
3. 实现“X of Y”的序号显示逻辑
因为你用了ConcatenatingMediaSource拼接多个视频,所以可以通过player.getCurrentMediaItemIndex()获取当前播放的视频索引(注意索引从0开始),总数量就是urlList.size()。然后在这些时机更新UI显示:
- 播放器Timeline变化时(
onTimelineChanged) - 播放位置跳变/切换视频时(
onPositionDiscontinuity) - 播放器进入就绪状态时(
onPlayerStateChanged)
修改后的完整代码示例
修正后的EventListener
Player.EventListener eventListener = new Player.EventListener() { @Override public void onTimelineChanged(Timeline timeline, Object manifest) { // 当Timeline变化(比如加载完序列视频)时更新序号 updatePlaybackSequence(); } @Override public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) { } @Override public void onLoadingChanged(boolean isLoading) { progressBar.setVisibility(isLoading ? View.VISIBLE : View.GONE); } @Override public void onPlayerStateChanged(boolean playWhenReady, int playbackState) { // 播放器就绪时更新序号 if (playbackState == Player.STATE_READY) { updatePlaybackSequence(); } } @Override public void onRepeatModeChanged(int repeatMode) { } @Override public void onPlayerError(ExoPlaybackException error) { } @Override public void onPositionDiscontinuity() { // 切换上/下一个视频时更新序号 updatePlaybackSequence(); } @Override public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) { } }; // 新增更新序号的工具方法 private void updatePlaybackSequence() { if (player != null && urlList != null && !urlList.isEmpty()) { int currentIndex = player.getCurrentMediaItemIndex() + 1; // 转成从1开始的序号 int totalCount = urlList.size(); // 替换成你的TextView控件,显示"1 of 6"这类文本 sequenceTextView.setText(String.format("%d of %d", currentIndex, totalCount)); } }
修正后的initializePlayer方法
private void initializePlayer() { simpleExoPlayerView.requestFocus(); // 复用已创建的player,避免重复实例化 if (player == null) { bandwidthMeter = new DefaultBandwidthMeter(); TrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory(bandwidthMeter); trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory); LoadControl loadControl = new DefaultLoadControl(); player = ExoPlayerFactory.newSimpleInstance(this, trackSelector, loadControl); simpleExoPlayerView.setPlayer(player); // 确保监听器绑定到当前的player实例 player.addListener(eventListener); } MediaSource[] mediaSources = new MediaSource[urlList.size()]; for (int i = 0; i < urlList.size(); i++) { mediaSources[i] = buildMediaSource(Uri.parse(urlList.get(i))); } MediaSource mediaSource = mediaSources.length == 1 ? mediaSources[0] : new ConcatenatingMediaSource(mediaSources); player.prepare(mediaSource); player.setPlayWhenReady(true); // 准备完成后自动播放 }
修正后的onCreate方法
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_video); ButterKnife.bind(this); presenter = new VideosPresenter(this); simpleExoPlayerView = findViewById(R.id.player_view); mediaDataSourceFactory = new DefaultDataSourceFactory(this, Util.getUserAgent(this, getString(R.string.app_name)), null); presenter.getVideoUrls(); // 移除提前创建player的逻辑,统一交给initializePlayer处理 }
额外注意事项
- 记得在Activity销毁时释放player资源,避免内存泄漏:
@Override protected void onDestroy() { super.onDestroy(); if (player != null) { player.release(); player = null; } }
- 如果你的ExoPlayer版本是2.16+,
ExoPlayerFactory已被废弃,建议改用ExoPlayer.Builder创建实例:
player = new ExoPlayer.Builder(this) .setTrackSelector(trackSelector) .setLoadControl(loadControl) .build();
内容的提问来源于stack exchange,提问作者Ashish Tiwari

