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

ExoPlayer在Fragment中旋转设备后无法调用seekTo()问题求助

解决ExoPlayer旋转后无法恢复播放进度的问题

我看了你的代码,发现几个关键问题导致旋转设备后没法正确恢复播放进度,咱们一步步来修正:

问题分析

  1. 状态保存顺序错误:你在onSaveInstanceState里先调用了releasePlayer(),再保存状态,但releasePlayer()里才把当前播放位置赋值给startPosition,这会导致保存的是旧值(甚至是初始值)。
  2. 基本类型判断错误:startPosition是long基本类型,不能用!= null判断,应该用ExoPlayer提供的C.TIME_UNSET来标记初始状态。
  3. 恢复逻辑时机不对:你在prepare之前调用seekTo(),这时候播放器还没加载媒体资源,无法生效;同时没有正确恢复playWhenReady的播放状态。
  4. 重复初始化问题: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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 06:41:57