【问题标题】:Android 9.+ API 29: /storage/emulated/0/Pictures/myPic.png open failed: EACCES (Permission Denied)Android 9.+ API 29:/storage/emulated/0/Pictures/myPic.png 打开失败:EACCES(权限被拒绝)
【发布时间】:2019-07-11 15:29:28
【问题描述】:

我正在使用 Android Virtual Device (AVD) Nexus 7 (2012) API 29 在 Android Studio 3.4.1 上尝试一个非常基本的相机应用程序的源代码。我在模拟器中用相机拍照并尝试保存它为 /storage/emulated/0/Pictures/myPic.png,但出现 /storage/emulated/0/Pictures/myPic.png 异常打开失败:EACCES(权限被拒绝)。我已经尝试了所有在线可用的建议,例如:

  1. 在AndroidManifest.xml中,我添加了:
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

  1. 在 MainActivity.java 中,我添加了以下运行时权限检查和请求代码:
public class MainActivity extends Activity {

    private static final int REQUEST_EXTERNAL_STORAGE = 1;
    private static String[] PERMISSIONS_STORAGE = {
            Manifest.permission.READ_EXTERNAL_STORAGE,
            Manifest.permission.WRITE_EXTERNAL_STORAGE
    };

    public static void verifyStoragePermissions(Activity activity) {
        int permission = ActivityCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE);

        boolean checkPermission = (permission == PackageManager.PERMISSION_GRANTED);

        Toast toastPermission = Toast.makeText(activity,
                "Is permission granted? " + checkPermission,
                Toast.LENGTH_SHORT);
        LinearLayout toastLayoutPermission = (LinearLayout) toastPermission.getView();
        TextView toastTVPermission = (TextView) toastLayoutPermission.getChildAt(0);
        toastTVPermission.setTextSize(30);
        toastPermission.show();


        if (permission != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(
                    activity,
                    PERMISSIONS_STORAGE,
                    REQUEST_EXTERNAL_STORAGE
            );
        }
    }

  1. 然后,我在 onCreate 和我想保存图片之前检查并请求权限,如下所示:
protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        verifyStoragePermissions(this);


......


private void saveImageToFile(File file)
    {
        if (mCameraBitmap != null)
        {
            FileOutputStream outStream = null;

            verifyStoragePermissions(this);

            try
            {
                outStream = new FileOutputStream(file);

                ...

            }
            catch (Exception e)
            {
                Toast toastException = Toast.makeText(MainActivity.this,
                        "Exception: " + e.getMessage(),
                        Toast.LENGTH_SHORT);
                LinearLayout toastLayoutException = (LinearLayout) toastException.getView();
                TextView toastTVException = (TextView) toastLayoutException.getChildAt(0);
                toastTVException.setTextSize(30);
                toastException.show();
            }

...

  1. 我还在模拟器中手动检查了存储权限,以确保允许访问存储。

但是,这些建议都不起作用!

代码抛出异常

/storage/emulated/0/Pictures/myPic.png 打开失败:EACCES(权限被拒绝)

当它到达

的行时
outStream = new FileOutputStream(file);

有什么想法可以进一步解决这个问题吗?非常感谢。

【问题讨论】:

    标签: android-studio


    【解决方案1】:

    android:requestLegacyExternalStorage="true" 添加到 Android Manifest 对我来说适用于 Android 29

    <application
        android:name=".MyApplication"
        android:allowBackup="true"
        android:requestLegacyExternalStorage="true" ...
    

    【讨论】:

      【解决方案2】:

      您的问题是由于在 API 29 中引入了scoped storage。您对该问题有三个解决方案(已经提到了一些,但我认为这将有助于澄清和巩固):

      1. 仅在运行 API 28 及更低版本的设备上运行您的应用。

      2. android:requestLegacyExternalStorage="true" 添加到 AndroidManifest.xml,以便您的应用可以在 API 29 及更高版本上运行:

      3. 针对 API 29+ 对您的应用进行现代化改造。首先摆脱 WRITE_EXTERNAL_STORAGE 权限。并将对已弃用的Environment.getExternalStoragePublicDirectory() 的调用替换为getExternalFilesDir(),这会将您的图像保存在外部存储中,但保存在只有您的应用可以访问的目录中。

      以下是使用 MediaStore 将位图保存为 JPG 的方法,它允许其他应用访问该图像:

      private void saveImage(Bitmap bitmap) throws IOException {
          ContentValues contentValues = new ContentValues();
          contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, "myImage.jpg");
          contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg");
          contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES);
      
          ContentResolver resolver = getContentResolver();
          Uri uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
          OutputStream imageOutStream = null;
      
          try {
              if (uri == null) {
                  throw new IOException("Failed to insert MediaStore row");
              }
      
              imageOutStream = resolver.openOutputStream(uri);
              if (!bitmap.compress(Bitmap.CompressFormat.JPEG, 100, imageOutStream)) {
                  throw new IOException("Failed to compress bitmap");
              }
          }
          finally {
              if (imageOutStream != null) {
                  imageOutStream.close();
              }
          }
      }
      

      【讨论】:

        【解决方案3】:

        我的项目中也出现了这个错误。

        我通过添加修复它

        android:requestLegacyExternalStorage="true"
        

        在清单文件中。 (see here)

        【讨论】:

          【解决方案4】:

          我还尝试使用相机并将照片保存到 ImageView 并遇到权限问题。我将 API 从 29 更改为 28 - 没有帮助,添加

          android:requestLegacyExternalStorage="true"
          

          没有帮助。 我更改了用于创建文件以存储图像的方法:

          File storageDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
          

          到:

          File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
          

          问题就解决了。顺便说一句,我不知道为什么。可能是因为第一个已被弃用。

          【讨论】:

            【解决方案5】:

            我最终发现问题出在Android API 29,一旦我降级到API 28,就没有权限问题了。

            【讨论】:

            • 谢谢,这解决了我的问题。正在研究 API 29,无论我尝试什么,我一直遇到同样的问题。降级到 API 28 修复了它
            • 降级只会延迟问题,将 android:requestLegacyExternalStorage="true" 添加到您的 Android 清单中,请参阅上面的答案
            【解决方案6】:

            android:requestLegacyExternalStorage="true" 添加到您的 Android 清单中

            <manifest ... >  
            <application android:requestLegacyExternalStorage="true" ... />
            </manifest>
            

            这个原因,你可以在https://medium.com/@sriramaripirala/android-10-open-failed-eacces-permission-denied-da8b630a89df阅读

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2016-10-15
              • 2021-05-15
              • 2015-06-27
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2021-12-23
              • 2021-07-02
              相关资源
              最近更新 更多