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

Android SurfaceView 播放本地影片 IOException:缺少儲存權限

     編輯:程式設計疑難解答

現象

程式碼邏輯正確,File.exists() 為真,但點擊播放後 setDataSource() 或 prepare() 拋出 IOException。

根本原因

Android 6.0 (API 23) 以上執行階段權限機制:僅在 Manifest 宣告權限不足,必須動態請求使用者授權。READ_EXTERNAL_STORAGE 未授權時,MediaPlayer 無法讀取外部儲存體檔案,拋出 IOException。

解決方案

  1. AndroidManifest.xml 宣告:
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <!-- 如需寫入 -->
  2. 執行階段請求權限 (API 23+):
    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();
            }
        }
    }
  3. Android 10 (API 29) 以上:優先使用 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();
    }
}

除錯技巧

  • Logcat 過濾 MediaPlayer 標籤,觀察詳細錯誤碼(如 MEDIA_ERROR_IO)。
  • 使用 adb shell am dumpheap 檢查檔案描述符洩漏。
  • 大檔案建議改用 ExoPlayer (Media3),支援串流、斷點續傳、硬體解碼回退。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved