BitmapFactory:无法解码流:java.io.FileNotFoundException:打开失败:Android Q 上的 EACCES(权限被拒绝)

Posted

技术标签:

【中文标题】BitmapFactory:无法解码流:java.io.FileNotFoundException:打开失败:Android Q 上的 EACCES(权限被拒绝)【英文标题】:BitmapFactory: Unable to decode stream: java.io.FileNotFoundException:open failed: EACCES (Permission denied) on Android Q 【发布时间】:2020-05-17 11:37:18 【问题描述】:

我授予了权限,但BitmapFactory.decodeFile() 返回此错误:

E/BitmapFactory: Unable to decode stream: java.io.FileNotFoundException: /storage/emulated/0/DCIM/Camera/IMG_20200130_165131.jpg: open failed: EACCES (Permission denied)

我想从图库中取出最后一张图片并将其保存到另一个文件中。我在运行它的 android P 测试设备上对其进行了测试。但它在具有 android 版本 Q(API 级别 29)的 android 模拟器中返回此错误。这有什么问题?

这些是我的代码 sn-ps:

writeStoragePermissionGranted();

BitmapFactory.Options options = null;
Bitmap bm = null;

String[] projection = new String[]
            MediaStore.Images.ImageColumns._ID,
            MediaStore.Images.ImageColumns.DATA,
            MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME,
            MediaStore.Images.ImageColumns.DATE_TAKEN,
            MediaStore.Images.ImageColumns.MIME_TYPE,
            MediaStore.Images.ImageColumns.DISPLAY_NAME,
    ;

    final Cursor cursor = this.getContentResolver()
            .query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, projection, null,
                    null, MediaStore.Images.ImageColumns.DATE_TAKEN + " DESC");
      if (cursor.moveToFirst()) 
        final ImageView imageView = (ImageView) findViewById(R.id.pictureView);
        String imageLocation = cursor.getString(1);
        File imageFile = new File(imageLocation);

        if (imageFile.exists()) 
            options = new BitmapFactory.Options();
            options.inSampleSize = 2;

            try 
                    bm = BitmapFactory.decodeFile(imageLocation, options);
             catch(Exception e) 
                    e.printStackTrace();
            
        

        imageView.setImageBitmap(bm);



        try 
            saveImage(bm,"NAME" );
         catch (IOException e) 
                e.printStackTrace();
        
    

writeStoragePermissionGranted()函数:

 public void writeStoragePermissionGranted() 
       if (Build.VERSION.SDK_INT >= 23) 
           if (checkSelfPermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
                   == PackageManager.PERMISSION_GRANTED) 
            startPeriodicRequest();
         else 
            ActivityCompat.requestPermissions(this, new String[]Manifest.permission.WRITE_EXTERNAL_STORAGE, Constants.REQUEST_CODE_WRITE_EXTERNAL_STORAGE_PERMISSION);
        
     else 
        return;
    
 

和清单权限:

 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
 <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

我知道对此有很多问题。但是没有人在 android 模拟器版本 Q 上工作。另一方面,此代码适用于 android pie(API 级别 28)

【问题讨论】:

在 Android Q 上,该路径不再可达。不要使用 DATA 而是使用 RELATIVE_PATH。 @blackapps 你能解释的更清楚点吗?也许举个例子? 【参考方案1】:

android:requestLegacyExternalStorage="true" 添加到清单的应用程序块实际上可以解决问题!

【讨论】:

谢谢这解决了我的问题,简单又干净 谢谢,它节省了我的时间... +1 从我身边 这是更多用例的链接developer.android.com/training/data-storage#scoped-storage您可能需要它 requestLegacyExternalStorage 是一个临时修复。面向 Android 11 的应用将忽略此属性。它旨在让开发人员有时间迁移到更新的实现 在 targetsdk 30 requestLegacyExternalStorage 将忽略。你更新了吗?【参考方案2】:

在 Android 10 上,他们引入了“Scoped Storage”的概念,这样,您就不能再使用其路径打开图像了。您可以获取更多信息HERE。

所以现在你必须使用它的ParcelFileDescriptor 和它的Uri 对其进行解码。

你可以:

final Cursor cursor = this.getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                        projection, null, null, MediaStore.Images.ImageColumns.DATE_TAKEN + " DESC");
if (cursor.moveToFirst()) 
    final ImageView imageView = (ImageView) findViewById(R.id.pictureView);


    if (Build.VERSION.SDK_INT >= 29) 
        // You can replace '0' by 'cursor.getColumnIndex(MediaStore.Images.ImageColumns._ID)'
        // Note that now, you read the column '_ID' and not the column 'DATA'
        Uri imageUri= ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, cursor.getInt(0));

        // now that you have the media URI, you can decode it to a bitmap
        try (ParcelFileDescriptor pfd = this.getContentResolver().openFileDescriptor(mediaUri, "r")) 
            if (pfd != null) 
                bitmap = BitmapFactory.decodeFileDescriptor(pfd.getFileDescriptor());
            
         catch (IOException ex) 

        
     else 
        // Repeat the code you already are using
    

【讨论】:

您好,使用此代码,如何将文件复制到某处以进行进一步处理?【参考方案3】:

您只需要在Manifest.xml 中将android:requestLegacyExternalStorage="true" 添加到Application 标签即可。

希望下面的代码对你有所帮助。

Manifest.xml

在清单文件中添加以下内容:

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />

另加:

将此添加到 Manifest 的 Application 标记中。

android:requestLegacyExternalStorage="true"

打开图库获取图片:

private void openImagePicker() 
    MyDatePicker datePicker = new MyDatePicker();
    datePicker.show(getSupportFragmentManager(), "DATE PICK");

启动 ActivityForResult:

onActivityResult你会得到结果对象,你会得到图像原始uri。

ActivityResultLauncher<Intent> galleryResultLauncher = registerForActivityResult(
        new ActivityResultContracts.StartActivityForResult(),
        new ActivityResultCallback<ActivityResult>() 
            @Override
            public void onActivityResult(ActivityResult result) 
                if (result.getResultCode() == Activity.RESULT_OK) 
                    Intent data = result.getData();
                    imageURi = data.getData();
                    image.setVisibility(View.VISIBLE);
                    String path = getPath(imageURi);
                    File file = new File(path);
                    if (file.exists()) 
                        Bitmap myBitmap = BitmapFactory.decodeFile(file.getAbsolutePath());
                        image.setImageBitmap(myBitmap);
                    
                
            
        );

获取图片的绝对路径:

public String getPath(Uri uri) 
    String[] projection = MediaStore.Images.Media.DATA;
    Cursor cursor = managedQuery(uri, projection, null, null, null);
    if (cursor != null) 
        int column_index = cursor
                .getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
        cursor.moveToFirst();
        return cursor.getString(column_index);
     else return null;

【讨论】:

以上是关于BitmapFactory:无法解码流:java.io.FileNotFoundException:打开失败:Android Q 上的 EACCES(权限被拒绝)的主要内容,如果未能解决你的问题,请参考以下文章

BitmapFactory:无法解码流:java.io.FileNotFoundException:打开失败:Android Q 上的 EACCES(权限被拒绝)

BitmapFactory 无法解码流。致命的空指针错误

Skia 解码器无法解码远程流

无法解码流:FileNotFoundException

BitmapFactory.decodeStream返回null的 ,InputStream 被调用两次,第一次调用流被关闭清空了!!!

BitmapFactory.decodeStream 总是返回 null,skia 解码器显示解码返回 false