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

时间:2019-07-11 15:29:28

标签: android-studio

我正在尝试使用Android虚拟设备(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);

有进一步解决这个问题的想法吗?非常感谢。

6 个答案:

答案 0 :(得分:5)

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

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

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

    <应用程序 android:requestLegacyExternalStorage =“ true” ...

  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();
        }
    }
}

答案 1 :(得分:4)

在Android Manifest中添加 android:requestLegacyExternalStorage =“ true” 对我来说适用于Android 29

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

答案 2 :(得分:4)

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

android:requestLegacyExternalStorage="true"

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

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

收件人:

File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);

问题消失了。顺便说一句,我不知道为什么。也许是因为第一个不推荐使用。

答案 3 :(得分:0)

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

答案 4 :(得分:0)

我的项目中也出现此错误。

我通过添加

对其进行了修复
android:requestLegacyExternalStorage="true"
清单文件中的

(see here)

答案 5 :(得分:0)

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

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

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