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

Android MediaPlayer在SDK<24版本恢复播放时崩溃求助

解决低版本SDK(<24)下MediaPlayer空指针崩溃问题

嘿,我帮你捋捋这个低版本SDK下MediaPlayer空指针的问题——我之前也碰到过类似的坑,大概率是状态管理和初始化时机在低版本下的严格要求导致的。SDK24及以上对MediaPlayer的状态校验做了优化,但低版本里稍不注意就会踩空指针的坑,咱们一步步来解决:

核心原因分析

低版本Android(<24)中MediaPlayer的状态机更敏感,而且系统对后台资源的回收更激进:

  • 如果你的mMediaPlayer实例化时机晚于监听器设置,或者在Activity后台被回收后没有重新初始化,就会触发空指针;
  • onResume时直接调用start(),没有先检查mMediaPlayer是否处于有效状态(比如是否被释放、是否已准备好)。

具体解决方案

1. 确保MediaPlayer初始化时机正确

把mMediaPlayer的实例化放在所有操作之前,并且用异步准备避免阻塞主线程,同时添加异常兜底:

private MediaPlayer mMediaPlayer;
private int mLastPosition = 0;
private String path; // 你的视频路径

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_player);
    
    // 先恢复上次保存的播放位置(如果有)
    if (savedInstanceState != null) {
        mLastPosition = savedInstanceState.getInt("last_play_position", 0);
    }
    
    // 优先实例化MediaPlayer,再执行后续配置
    initMediaPlayer();
}

// 把初始化逻辑抽成独立方法,方便复用
private void initMediaPlayer() {
    // 先释放旧的实例,避免内存泄漏
    if (mMediaPlayer != null) {
        mMediaPlayer.release();
        mMediaPlayer = null;
    }
    
    mMediaPlayer = new MediaPlayer();
    try {
        mMediaPlayer.setOnVideoSizeChangedListener(this);
        mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
        mMediaPlayer.setDataSource(this, Uri.parse(path));
        
        // 准备完成后自动恢复播放位置
        mMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
            @Override
            public void onPrepared(MediaPlayer mp) {
                if (mLastPosition > 0) {
                    mp.seekTo(mLastPosition);
                }
                mp.start();
            }
        });
        
        mMediaPlayer.setOnCompletionListener(this);
        // 用异步准备,避免阻塞主线程
        mMediaPlayer.prepareAsync();
    } catch (Exception e) {
        e.printStackTrace();
        // 这里可以加个用户提示,比如"视频加载失败"
    }
}

2. 优化生命周期中的状态管理

在onPause和onResume中严格校验mMediaPlayer的状态,避免空指针:

@Override
protected void onPause() {
    super.onPause();
    // 记录位置前先检查实例是否存在、是否正在播放
    if (mMediaPlayer != null && mMediaPlayer.isPlaying()) {
        mLastPosition = mMediaPlayer.getCurrentPosition();
        mMediaPlayer.pause();
    }
}

@Override
protected void onResume() {
    super.onResume();
    if (mMediaPlayer != null) {
        // 检查MediaPlayer是否已准备好,且未在播放
        if (mMediaPlayer.isPrepared() && !mMediaPlayer.isPlaying()) {
            mMediaPlayer.seekTo(mLastPosition);
            mMediaPlayer.start();
        }
    } else {
        // 如果实例已被回收,重新初始化
        initMediaPlayer();
    }
}

3. 处理配置变化(比如屏幕旋转)

低版本中屏幕旋转会重建Activity,导致mMediaPlayer被销毁,你可以在Manifest里给Activity添加配置,避免重建:

<activity
    android:name=".YourPlayerActivity"
    android:configChanges="orientation|screenSize|keyboardHidden" />

或者在onSaveInstanceState中保存播放位置,重建后恢复:

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putInt("last_play_position", mLastPosition);
}

4. 全局空指针防护

在所有使用mMediaPlayer的回调和方法里,都加上非空判断:

@Override
public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
    if (mMediaPlayer == null) return;
    // 你的视频尺寸适配逻辑
}

@Override
public void onCompletion(MediaPlayer mp) {
    if (mMediaPlayer == null) return;
    // 播放完成后的逻辑,比如重置位置
    mLastPosition = 0;
}

最后提醒

低版本SDK下一定要遵循MediaPlayer的状态机规则:未初始化的实例不能调用任何方法,已释放的实例不能复用。每次操作前都检查实例状态,就能避免大部分空指针问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 03:14:26