ExoPlayer在Fragment中旋转设备后无法调用seekTo()问题求助
解决ExoPlayer旋转后无法恢复播放进度的问题
我看了你的代码,发现几个关键问题导致旋转设备后没法正确恢复播放进度,咱们一步步来修正:
问题分析
- 状态保存顺序错误:你在
onSaveInstanceState里先调用了releasePlayer(),再保存状态,但releasePlayer()里才把当前播放位置赋值给startPosition,这会导致保存的是旧值(甚至是初始值)。 - 基本类型判断错误:
startPosition是long基本类型,不能用!= null判断,应该用ExoPlayer提供的C.TIME_UNSET来标记初始状态。 - 恢复逻辑时机不对:你在
prepare之前调用seekTo(),这时候播放器还没加载媒体资源,无法生效;同时没有正确恢复playWhenReady的播放状态。 - 重复初始化问题:
onCreateView里直接调用initializePlayer(),此时onViewStateRestored还没执行,恢复的状态还没赋值,导致初始化时用的是初始值。
解决方案
1. 初始化变量默认值
在Fragment类里给状态变量设置正确的初始值:
import com.google.android.exoplayer2.C; // 替换你的变量定义 private long startPosition = C.TIME_UNSET; private int startWindow = 0; private boolean playWhenReady = true; // 移除startAutoPlay变量,统一用playWhenReady
2. 修正状态保存逻辑
先更新当前播放状态,再保存到Bundle,最后释放播放器:
private static final String KEY_PLAY_WHEN_READY = "play_when_ready"; private static final String KEY_WINDOW = "window"; private static final String KEY_POSITION = "position"; @Override public void onSaveInstanceState(Bundle outState) { // 先更新当前播放状态 updateStartPosition(); // 保存状态到Bundle outState.putBoolean(KEY_PLAY_WHEN_READY, playWhenReady); outState.putInt(KEY_WINDOW, startWindow); outState.putLong(KEY_POSITION, startPosition); // 必须调用父类方法 super.onSaveInstanceState(outState); // 最后释放播放器 releasePlayer(); }
3. 修正状态恢复逻辑
在onViewStateRestored里正确恢复保存的状态:
@Override public void onViewStateRestored(@Nullable Bundle savedInstanceState) { super.onViewStateRestored(savedInstanceState); if (savedInstanceState != null) { playWhenReady = savedInstanceState.getBoolean(KEY_PLAY_WHEN_READY); startWindow = savedInstanceState.getInt(KEY_WINDOW); startPosition = savedInstanceState.getLong(KEY_POSITION); } }
4. 修正播放器初始化逻辑
把恢复进度的逻辑放到prepare之后,并且正确恢复播放状态:
public void initializePlayer(String mediaUri) { if (mExoPlayer == null && mediaUri != null) { BandwidthMeter bandwidthMeter = new DefaultBandwidthMeter(); TrackSelection.Factory videoTrackSelectionFactory = new AdaptiveTrackSelection.Factory(bandwidthMeter); trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory); mExoPlayer = ExoPlayerFactory.newSimpleInstance(getContext(), trackSelector); mPlayerView.setPlayer(mExoPlayer); mediaDataSourceFactory = new DefaultDataSourceFactory( getContext(), Util.getUserAgent(getContext(), "BakingX"), (TransferListener<? super DataSource>) bandwidthMeter); mediaSource = new ExtractorMediaSource .Factory(mediaDataSourceFactory) .createMediaSource(Uri.parse(mediaUri)); // 先加载媒体资源 mExoPlayer.prepare(mediaSource); // 只有当保存的位置不是初始值时,才恢复进度 if (startPosition != C.TIME_UNSET) { mExoPlayer.seekTo(startWindow, startPosition); } // 恢复自动播放状态 mExoPlayer.setPlayWhenReady(playWhenReady); } }
5. 移除重复初始化
在onCreateView里移除initializePlayer(videoUrl)调用,交给onStart/onResume的生命周期方法处理,确保状态恢复后再初始化播放器:
@Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view= inflater.inflate(R.layout.fragment_recipe_steps_video_player, container, false); ButterKnife.bind(this, view); Bundle bundle=getArguments(); if(bundle!=null){ videoUrl=bundle.getString(VIDEO_URL_KEY); stepDescription.setText(bundle.getString(STEP_DESCRIPTION_KEY)); } // 移除这里的initializePlayer调用 return view; }
6. 修正updateStartPosition方法
用getCurrentPosition()获取当前播放位置(getContentPosition是媒体总时长,不是当前进度):
private void updateStartPosition() { if (mExoPlayer != null) { playWhenReady = mExoPlayer.getPlayWhenReady(); startWindow = mExoPlayer.getCurrentWindowIndex(); startPosition = Math.max(0, mExoPlayer.getCurrentPosition()); } }
关键要点总结
- 状态保存要遵循先更新→再保存→最后释放的顺序
- 用ExoPlayer的
C.TIME_UNSET标记初始位置,避免基本类型的判断错误 - 恢复进度必须在
prepare()之后执行,否则播放器无法定位到指定位置 - 确保初始化播放器前已经完成状态恢复,避免用初始值初始化
内容的提问来源于stack exchange,提问作者mohammed Abu El Majd
相关产品推荐
相关产品推荐

