首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計綜合問答

Android MediaPlayer 釋放資源避免 IllegalStateException

     編輯:程式設計綜合問答

問題現象

Activity 結束時釋放 MediaPlayer 拋出 IllegalStateException,堆疊追蹤指向 isPlaying()、getCurrentPosition() 被 MediaController 在釋放後呼叫。

錯誤代碼分析

// 按鈕點擊釋放
imageAllBack.setOnClickListener(v -> {
    if (mediaPlayer != null) {
        mediaPlayer.stop();
        mediaPlayer.release(); // 釋放後狀態重置
    }
    finish();
});

// onStop 再次釋放 (重複釋放)
@Override protected void onStop() {
    super.onStop();
    mediaPlayer.stop();
    mediaPlayer.release(); // 崩潰點:MediaController 仍在嘗試存取
}

核心原因:MediaController 內部有 Handler 定期輪詢 MediaPlayer 狀態 (更新進度條),釋放後仍在嘗試存取導致非法狀態。

正確釋放流程

private void releaseMediaPlayer() {
    if (mediaPlayer != null) {
        try {
            // 1. 先移除 MediaController 回調 (關鍵)
            if (mediaController != null) {
                mediaController.hide(); // 停止內部 Handler 輪詢
                mediaController.setMediaPlayer(null); // 斷開關聯
            }
            
            // 2. 停止並釋放
            if (mediaPlayer.isPlaying()) mediaPlayer.stop();
            mediaPlayer.release();
        } catch (IllegalStateException e) {
            e.printStackTrace(); // 防禦性捕獲
        } finally {
            mediaPlayer = null;
            mediaController = null;
        }
    }
}

// 在所有離開點統一呼叫
@Override protected void onStop() {
    super.onStop();
    releaseMediaPlayer();
}

public void onBackClick(View v) {
    releaseMediaPlayer();
    finish();
}

完整生命週期管理模板

public class VideoActivity extends AppCompatActivity implements MediaPlayerControl {
    private MediaPlayer mediaPlayer;
    private MediaController mediaController;
    
    @Override protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        initMediaPlayer();
    }
    
    private void initMediaPlayer() {
        mediaPlayer = MediaPlayer.create(this, R.raw.video);
        mediaController = new MediaController(this);
        mediaController.setMediaPlayer(this);
        mediaController.setAnchorView(findViewById(R.id.video_view));
        mediaPlayer.setOnPreparedListener(mp -> mediaController.show());
    }
    
    // MediaPlayerControl 介面實作
    @Override public void start() { mediaPlayer.start(); }
    @Override public void pause() { mediaPlayer.pause(); }
    @Override public int getDuration() { return mediaPlayer.getDuration(); }
    @Override public int getCurrentPosition() { return mediaPlayer.getCurrentPosition(); }
    @Override public void seekTo(int pos) { mediaPlayer.seekTo(pos); }
    @Override public boolean isPlaying() { return mediaPlayer.isPlaying(); }
    @Override public int getBufferPercentage() { return 0; }
    @Override public boolean canPause() { return true; }
    @Override public boolean canSeekBackward() { return true; }
    @Override public boolean canSeekForward() { return true; }
    
    @Override protected void onDestroy() {
        super.onDestroy();
        releaseMediaPlayer();
    }
    
    private void releaseMediaPlayer() {
        if (mediaController != null) {
            mediaController.hide();
            mediaController.setMediaPlayer(null);
        }
        if (mediaPlayer != null) {
            try {
                if (mediaPlayer.isPlaying()) mediaPlayer.stop();
                mediaPlayer.release();
            } catch (Exception e) { e.printStackTrace(); }
            finally { mediaPlayer = null; }
        }
    }
}

關鍵原則

  1. 單一釋放點:統一在 onDestroy() 或自訂 release() 方法
  2. 先斷開 Controller:mediaController.setMediaPlayer(null) 停止內部輪詢
  3. 防禦性程式:try-catch 包裹 release(),檢查 isPlaying() 前確認非 null
  4. 設為 null:釋放後將引用設為 null 防止重複操作
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved