程式碼邏輯正確,File.exists() 為真,但點擊播放後 setDataSource() 或 prepare() 拋出 IOException。
Android 6.0 (API 23) 以上執行階段權限機制:僅在 Manifest 宣告權限不足,必須動態請求使用者授權。READ_EXTERNAL_STORAGE 未授權時,MediaPlayer 無法讀取外部儲存體檔案,拋出 IOException。
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <!-- 如需寫入 -->
private static final int REQUEST_STORAGE = 100;
@Override
protected void onCreate(Bundle savedInstanceState) {
// ...
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
REQUEST_STORAGE);
} else {
initPlayer(); // 已授權直接初始化
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if (requestCode == REQUEST_STORAGE) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
initPlayer();
} else {
Toast.makeText(this, "需要儲存權限才能播放影片", Toast.LENGTH_LONG).show();
}
}
}
MediaStore 或 Scoped Storage 存取共用媒體檔案,不再需要額外權限。playButton.setOnClickListener(v -> {
if (checkStoragePermission()) {
startPlayback();
}
});
private void startPlayback() {
mediaPlayer.reset();
try {
mediaPlayer.setDataSource(file.getAbsolutePath());
mediaPlayer.setDisplay(surfaceView.getHolder());
mediaPlayer.prepareAsync(); // 非阻塞準備
mediaPlayer.setOnPreparedListener(mp -> {
mp.start();
updateButtonState(true);
});
} catch (IOException e) {
Log.e("TAG", "Playback error", e);
Toast.makeText(this, "播放失敗: " + e.getMessage(), Toast.LENGTH_SHORT).show();
}
}
MediaPlayer 標籤,觀察詳細錯誤碼(如 MEDIA_ERROR_IO)。adb shell am dumpheap 檢查檔案描述符洩漏。