我试图使用BitmapFactory从/ storage / emulated / 0 / Pictures查看图像-但看到错误渗透被拒绝

问题描述

我在Android Studio中启动了一个新项目,该项目包括:空活动,Java,API 29,Android 10.0(Q)

我向AndroidManifest.xml添加了权限

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

我在activity_main.xml中添加了ImageView和一个按钮

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="80dp"
        android:text="button"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <ImageView
        android:id="@+id/my_image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/button"
        />

我在MainActivity.java中加载了一个图像-单击按钮后,将在运行时检查权限。然后,在另一个按钮上单击后,应查看图像。该代码还包含对正确活动的一些检查。

package mrppanther.com.example.read_image;

import android.Manifest;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import java.io.File;

public class MainActivity extends AppCompatActivity {
    private int STORAGE_PERMISSION_CODE = 1;
    private int new_state = 1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // NOTE: use a button to request activity
        Button buttonRequest = findViewById(R.id.button);
        buttonRequest.setonClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                // NOTE: declare "myfile",this is want I want to view
                File myfile = new File("/storage/emulated/0/Pictures/henry1.jpg");

                // NOTE: on the 1st click,check permissions
                if (new_state == 1) {
                    requestStoragePermission();
                    new_state = 2; // NOTE: move to the next state,on the next click

                // NOTE: on the 2nd click,view image
                } else {

                    // NOTE: check that the "myfile"" exists
                    if (myfile.exists()) {
                        Log.i("myfile","FILE EXISTS PASS! " + myfile);
                    } else {
                        Log.i("myfile","FILE ABSENT FAIL! " + myfile);
                    }

                    // NOTE: declare "myimage"
                    ImageView myimage = (ImageView) findViewById(R.id.my_image);

                    // NOTE: experimenting with options did not help
                    BitmapFactory.Options options=new BitmapFactory.Options();
                    //options.inJustDecodeBounds=true;
                    options.inSampleSize=1;

                    // ERROR: E/BitmapFactory: Unable to decode stream: java.io.FileNotFoundException:
                    // ERROR: /storage/emulated/0/Pictures/henry1.jpg: open Failed: EACCES (Permission denied)
                    Bitmap bitmap = BitmapFactory.decodeFile(String.valueOf(myfile),options);

                    myimage.setimageBitmap(bitmap);

                    // NOTE: here is a test to check "myimage" is correct
                    // NOTE: this works OK,and image from drawable resource is displayed
                    // myimage.setimageResource(R.drawable.henry1);

                    new_state = 1; // NOTE: go back to the 1st state,on the next click
                }
            }
        }); // NOTE: this is the end of button activity
    }

    // NOTE: request permission to read storage,at run time
    private void requestStoragePermission() {
        ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.READ_EXTERNAL_STORAGE,Manifest.permission.WRITE_EXTERNAL_STORAGE},STORAGE_PERMISSION_CODE);
        // NOTE: here is a test which sets the wrong permission
        // new String[]{Manifest.permission.READ_PHONE_NUMBERS,Manifest.permission.READ_PHONE_STATE},STORAGE_PERMISSION_CODE);
    }

    // NOTE: check read permission is granted,at run time
    @Override
    public void onRequestPermissionsResult(int requestCode,@NonNull String[] permissions,@NonNull int[] grantResults) {
        if (requestCode == STORAGE_PERMISSION_CODE) {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                Toast.makeText(this,"Permission GRANTED",Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(this,"Permission DENIED",Toast.LENGTH_SHORT).show();
            }
        }
    }
}

结果:我看到了以下错误消息,但未显示图像。 但是,运行时权限检查指示为“权限已授予”。最后,我重新启动了平板电脑。我检查了该应用是否被允许访问存储(在设置,应用,存储权限中)。我在断开USB连接的情况下重新运行了该应用程序,但未显示该图像。

I/myfile: FILE EXISTS PASS! /storage/emulated/0/Pictures/henry1.jpg
E/BitmapFactory: Unable to decode stream: java.io.FileNotFoundException: /storage/emulated/0/Pictures/henry1.jpg: open Failed: EACCES (Permission denied)

解决方法

在Android 10及更高版本中,您只能将File类用于本地私有目录中的媒体。无法使用文件路径直接访问外部媒体。

对于公共媒体,您必须使用媒体商店中的实际媒体Uri。对于您的具体情况,请执行以下操作:

  1. 使用媒体商店获取目标图片Uri
  2. Open a Stream定位此类Uri。
  3. Use the stream与BitmapFactory。
  4. 别忘了关闭流。

备注:Media Store中的DATA字段已被弃用,它可能包含路径也可能不包含路径,因此,在涉及文件时,应避免使用路径,并遵守内容Uri的内容。