Marshmallow 中的存储权限错误

Storage permission error in Marshmallow

在 Lollipop 中,下载功能在我的应用程序中运行良好,但当我升级到 Marshmallow 时,我的应用程序崩溃并在我尝试从互联网下载到 SD 卡时出现此错误:

Neither user  nor current process has android.permission.WRITE_EXTERNAL_STORAGE

它抱怨这行代码:

DownloadManager manager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
manager.enqueue(request);

我在清单外部应用程序中拥有权限:

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

我清理并重建了项目,但它仍然崩溃。

您需要在 marshmallow 中使用运行时权限 https://developer.android.com/training/permissions/requesting.html

您可以查看应用信息 -> 权限

您的应用是否获得写入外部存储的权限

似乎用户拒绝了权限,应用试图写入外部磁盘,导致错误。

@Override
public void onRequestPermissionsResult(int requestCode,
        String permissions[], int[] grantResults) {
    switch (requestCode) {
        case MY_PERMISSIONS_REQUEST_READ_CONTACTS: {
            // If request is cancelled, the result arrays are empty.
            if (grantResults.length > 0
                && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

                // permission was granted, yay! Do the
                // contacts-related task you need to do.

            } else {

                // permission denied, boo! Disable the
                // functionality that depends on this permission.
            }
            return;
        }

        // other 'case' lines to check for other
        // permissions this app might request
    }
}

勾选https://developer.android.com/training/permissions/requesting.html

此视频将使您更好地了解用户体验、处理运行时权限https://www.youtube.com/watch?v=iZqDdvhTZj0

您应该使用以下方式检查用户是否已授予外部存储权限:

if (checkSelfPermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
    Log.v(TAG,"Permission is granted");
    //File write logic here
    return true;
}

如果没有,您需要请求用户授予您的应用权限:

ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_CODE);

当然这些仅适用于 Marshmallow 设备,因此您需要检查您的应用是否在 Marshmallow 上 运行:

 if (Build.VERSION.SDK_INT >= 23) {
      //do your check here
 }

还要确保您的 activity 实现了 OnRequestPermissionResult

整个权限如下所示:

public  boolean isStoragePermissionGranted() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        if (checkSelfPermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
                == PackageManager.PERMISSION_GRANTED) {
            Log.v(TAG,"Permission is granted");
            return true;
        } else {

            Log.v(TAG,"Permission is revoked");
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
            return false;
        }
    }
    else { //permission is automatically granted on sdk<23 upon installation
        Log.v(TAG,"Permission is granted");
        return true;
    }
}

权限结果回调:

@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if(grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){
        Log.v(TAG,"Permission: "+permissions[0]+ "was "+grantResults[0]);
        //resume tasks needing this permission
    }
}

Android's permission system is one of the biggest security concern all along since those permissions are asked for at install time. Once installed, the application will be able to access all of things granted without any user's acknowledgement what exactly application does with the permission.

Android 6.0 Marshmallow introduces one of the largest changes to the permissions model with the addition of runtime permissions, a new permission model that replaces the existing install time permissions model when you target API 23 and the app is running on an Android 6.0+ device

致谢 Requesting Permissions at Run Time .

示例

将此声明为全局

private static final int PERMISSION_REQUEST_CODE = 1;

将此添加到您的 onCreate() 部分

setContentView(R.layout.your_xml)之后;

 if (Build.VERSION.SDK_INT >= 23)
    {
        if (checkPermission())
        {
            // Code for above or equal 23 API Oriented Device 
            // Your Permission granted already .Do next code
        } else {
            requestPermission(); // Code for permission
        }
    }
  else
    {

       // Code for Below 23 API Oriented Device 
       // Do next code
    }

现在添加 checkPermission()requestPermission()

 private boolean checkPermission() {
    int result = ContextCompat.checkSelfPermission(Your_Activity.this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE);
    if (result == PackageManager.PERMISSION_GRANTED) {
        return true;
    } else {
        return false;
    }
}

private void requestPermission() {

    if (ActivityCompat.shouldShowRequestPermissionRationale(Your_Activity.this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
        Toast.makeText(Your_Activity.this, "Write External Storage permission allows us to do store images. Please allow this permission in App Settings.", Toast.LENGTH_LONG).show();
    } else {
        ActivityCompat.requestPermissions(Your_Activity.this, new String[]{android.Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_REQUEST_CODE);
    }
}

@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
    switch (requestCode) {
        case PERMISSION_REQUEST_CODE:
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                Log.e("value", "Permission Granted, Now you can use local drive .");
            } else {
                Log.e("value", "Permission Denied, You cannot use local drive .");
            }
            break;
    }
}

仅供参考

onRequestPermissionsResult

This interface is the contract for receiving the results for permission requests.

除非有明确的写入外部存储的需求,否则您始终可以选择将文件保存在app目录中。在我的例子中,我不得不保存文件,在浪费了 2 到 3 天后,我发现我是否将存储路径从

Environment.getExternalStorageDirectory()

getApplicationContext().getFilesDir().getPath() //which returns the internal app files directory path

它在所有设备上都非常有效。 这是因为在外部存储上写入需要额外的权限,但在内部应用程序目录中写入很简单。

经过大量搜索此代码对我有用:

查看已经拥有的权限: 检查 WRITE_EXTERNAL_STORAGE 权限是否允许?

if(isReadStorageAllowed()){
            //If permission is already having then showing the toast
            //Toast.makeText(SplashActivity.this,"You already have the permission",Toast.LENGTH_LONG).show();
            //Existing the method with return
            return;
        }else{
            requestStoragePermission();
        }



private boolean isReadStorageAllowed() {
    //Getting the permission status
    int result = ContextCompat.checkSelfPermission(this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE);

    //If permission is granted returning true
    if (result == PackageManager.PERMISSION_GRANTED)
        return true;

    //If permission is not granted returning false
    return false;
}

//Requesting permission
private void requestStoragePermission(){

    if (ActivityCompat.shouldShowRequestPermissionRationale(this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE)){
        //If the user has denied the permission previously your code will come to this block
        //Here you can explain why you need this permission
        //Explain here why you need this permission
    }

    //And finally ask for the permission
    ActivityCompat.requestPermissions(this,new String[]{android.Manifest.permission.WRITE_EXTERNAL_STORAGE},REQUEST_WRITE_STORAGE);
}

实现重写 onRequestPermissionsResult 方法以检查用户是允许还是拒绝

 @Override
 public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    //Checking the request code of our request
    if(requestCode == REQUEST_WRITE_STORAGE){

        //If permission is granted
        if(grantResults.length >0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){

            //Displaying a toast
            Toast.makeText(this,"Permission granted now you can read the storage",Toast.LENGTH_LONG).show();

        }else{
            //Displaying another toast if permission is not granted
            Toast.makeText(this,"Oops you just denied the permission",Toast.LENGTH_LONG).show();
        }
    }

对我有用

 boolean hasPermission = (ContextCompat.checkSelfPermission(AddContactActivity.this,
            Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED);
    if (!hasPermission) {
        ActivityCompat.requestPermissions(AddContactActivity.this,
                new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
                REQUEST_WRITE_STORAGE);
    }

   @Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    switch (requestCode)
    {
        case REQUEST_WRITE_STORAGE: {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED)
            {
                //reload my activity with permission granted or use the features what required the permission
            } else
            {
                Toast.makeText(AddContactActivity.this, "The app was not allowed to write to your storage. Hence, it cannot function properly. Please consider granting it this permission", Toast.LENGTH_LONG).show();
            }
        }
    }

}

检查 API 级别 23 中的多个权限 第 1 步:

 String[] permissions = new String[]{
        Manifest.permission.INTERNET,
        Manifest.permission.READ_PHONE_STATE,
        Manifest.permission.READ_EXTERNAL_STORAGE,
        Manifest.permission.WRITE_EXTERNAL_STORAGE,
        Manifest.permission.VIBRATE,
        Manifest.permission.RECORD_AUDIO,
};

第 2 步:

 private boolean checkPermissions() {
    int result;
    List<String> listPermissionsNeeded = new ArrayList<>();
    for (String p : permissions) {
        result = ContextCompat.checkSelfPermission(this, p);
        if (result != PackageManager.PERMISSION_GRANTED) {
            listPermissionsNeeded.add(p);
        }
    }
    if (!listPermissionsNeeded.isEmpty()) {
        ActivityCompat.requestPermissions(this, listPermissionsNeeded.toArray(new String[listPermissionsNeeded.size()]), 100);
        return false;
    }
    return true;
}

第 3 步:

 @Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
    if (requestCode == 100) {
        if (grantResults.length > 0
                && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            // do something
        }
        return;
    }
}

第 4 步: 在 Activity 的 onCreate 中 检查权限();

从 marshmallow 版本开始,开发者需要向用户请求运行时间权限。 让我告诉你请求 运行时间权限的整个过程。

我正在使用此处的参考资料:marshmallow runtime permissions android

首先创建一个方法来检查是否已授予所有权限

private  boolean checkAndRequestPermissions() {
        int camerapermission = ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA);
        int writepermission = ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
        int permissionLocation = ContextCompat.checkSelfPermission(this,Manifest.permission.ACCESS_FINE_LOCATION);
        int permissionRecordAudio = ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO);


        List<String> listPermissionsNeeded = new ArrayList<>();

        if (camerapermission != PackageManager.PERMISSION_GRANTED) {
            listPermissionsNeeded.add(Manifest.permission.CAMERA);
        }
        if (writepermission != PackageManager.PERMISSION_GRANTED) {
            listPermissionsNeeded.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
        }
        if (permissionLocation != PackageManager.PERMISSION_GRANTED) {
            listPermissionsNeeded.add(Manifest.permission.ACCESS_FINE_LOCATION);
        }
        if (permissionRecordAudio != PackageManager.PERMISSION_GRANTED) {
            listPermissionsNeeded.add(Manifest.permission.RECORD_AUDIO);
        }
        if (!listPermissionsNeeded.isEmpty()) {
            ActivityCompat.requestPermissions(this, listPermissionsNeeded.toArray(new String[listPermissionsNeeded.size()]), REQUEST_ID_MULTIPLE_PERMISSIONS);
            return false;
        }
        return true;
    } 

现在这是在上述方法之后 运行 的代码。我们将覆盖 onRequestPermissionsResult() 方法:

 @Override
    public void onRequestPermissionsResult(int requestCode,
                                           String permissions[], int[] grantResults) {
        Log.d(TAG, "Permission callback called-------");
        switch (requestCode) {
            case REQUEST_ID_MULTIPLE_PERMISSIONS: {

                Map<String, Integer> perms = new HashMap<>();
                // Initialize the map with both permissions
                perms.put(Manifest.permission.CAMERA, PackageManager.PERMISSION_GRANTED);
                perms.put(Manifest.permission.WRITE_EXTERNAL_STORAGE, PackageManager.PERMISSION_GRANTED);
                perms.put(Manifest.permission.ACCESS_FINE_LOCATION, PackageManager.PERMISSION_GRANTED);
                perms.put(Manifest.permission.RECORD_AUDIO, PackageManager.PERMISSION_GRANTED);
                // Fill with actual results from user
                if (grantResults.length > 0) {
                    for (int i = 0; i < permissions.length; i++)
                        perms.put(permissions[i], grantResults[i]);
                    // Check for both permissions
                    if (perms.get(Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED
                            && perms.get(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED 
&& perms.get(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED 
&& perms.get(Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED) {
                        Log.d(TAG, "sms & location services permission granted");
                        // process the normal flow
                        Intent i = new Intent(MainActivity.this, WelcomeActivity.class);
                        startActivity(i);
                        finish();
                        //else any one or both the permissions are not granted
                    } else {
                        Log.d(TAG, "Some permissions are not granted ask again ");
                        //permission is denied (this is the first time, when "never ask again" is not checked) so ask again explaining the usage of permission
//                        // shouldShowRequestPermissionRationale will return true
                        //show the dialog or snackbar saying its necessary and try again otherwise proceed with setup.
                        if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA) 
|| ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) 
|| ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.ACCESS_FINE_LOCATION)
 || ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.RECORD_AUDIO)) {
                            showDialogOK("Service Permissions are required for this app",
                                    new DialogInterface.OnClickListener() {
                                        @Override
                                        public void onClick(DialogInterface dialog, int which) {
                                            switch (which) {
                                                case DialogInterface.BUTTON_POSITIVE:
                                                    checkAndRequestPermissions();
                                                    break;
                                                case DialogInterface.BUTTON_NEGATIVE:
                                                    // proceed with logic by disabling the related features or quit the app.
                                                    finish();
                                                    break;
                                            }
                                        }
                                    });
                        }
                        //permission is denied (and never ask again is  checked)
                        //shouldShowRequestPermissionRationale will return false
                        else {
                            explain("You need to give some mandatory permissions to continue. Do you want to go to app settings?");
                            //                            //proceed with logic by disabling the related features or quit the app.
                        }
                    }
                }
            }
        }

    }

如果用户点击拒绝选项然后showDialogOK()方法将被用来显示对话框

如果用户单击 拒绝 并且 单击一个复选框 "never ask again",则 explain() 方法将用于显示对话框。

显示对话框的方法:

 private void showDialogOK(String message, DialogInterface.OnClickListener okListener) {
        new AlertDialog.Builder(this)
                .setMessage(message)
                .setPositiveButton("OK", okListener)
                .setNegativeButton("Cancel", okListener)
                .create()
                .show();
    }
    private void explain(String msg){
        final android.support.v7.app.AlertDialog.Builder dialog = new android.support.v7.app.AlertDialog.Builder(this);
        dialog.setMessage(msg)
                .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface paramDialogInterface, int paramInt) {
                        //  permissionsclass.requestPermission(type,code);
                        startActivity(new Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS, Uri.parse("package:com.exampledemo.parsaniahardik.marshmallowpermission")));
                    }
                })
                .setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface paramDialogInterface, int paramInt) {
                        finish();
                    }
                });
        dialog.show();
    }

以上代码片段一次请求四个权限。您还可以根据需要在 any activity 中请求任意数量的权限。

   Try this



int permission = ContextCompat.checkSelfPermission(MainActivity.this,
                    android.Manifest.permission.WRITE_EXTERNAL_STORAGE);

     if (permission != PackageManager.PERMISSION_GRANTED) {
                Log.i("grant", "Permission to record denied");

                if (ActivityCompat.shouldShowRequestPermissionRationale(this,
                        android.Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
                    AlertDialog.Builder builder = new AlertDialog.Builder(this);
                    builder.setMessage(getString(R.string.permsg))
                            .setTitle(getString(R.string.permtitle));

                    builder.setPositiveButton(getString(R.string.ok), new DialogInterface.OnClickListener() {

                        public void onClick(DialogInterface dialog, int id) {
                            Log.i("grant", "Clicked");
                            makeRequest();
                        }
                    });

                    AlertDialog dialog = builder.create();
                    dialog.show();

                } else {

                    //makeRequest1();
                    makeRequest();
                }
            }


     protected void makeRequest() {
            ActivityCompat.requestPermissions(this,
                    new String[]{android.Manifest.permission.WRITE_EXTERNAL_STORAGE},
                    500);
        }



     @Override
        public void onRequestPermissionsResult(int requestCode,
                                               String permissions[], int[] grantResults) {
            switch (requestCode) {
                case 500: {

                    if (grantResults.length == 0
                            || grantResults[0] !=
                            PackageManager.PERMISSION_GRANTED) {

                        Log.i("1", "Permission has been denied by user");

                    } else {

                        Log.i("1", "Permission has been granted by user");

                    }
                    return;
                }

            }
        }

在开始下载之前检查您的运行时权限,如果您没有权限,请使用此方法请求权限

requestStoragePermission()

private void requestStoragePermission(){
    if (ActivityCompat.shouldShowRequestPermissionRationale(this, 
                android.Manifest.permission.READ_EXTERNAL_STORAGE))
        {

        }

        ActivityCompat.requestPermissions(this, 
            new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
            STORAGE_PERMISSION_CODE);
}

@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){
        }
        else{
            Toast.makeText(this,
                           "Oops you just denied the permission", 
                           Toast.LENGTH_LONG).show();
        }
    }
}

可以使用 manifest.xml 文件以简单的方式授予权限,但是如果我们想获得权限,从这里到 api 级别 23 sdk 版本 6 之前都是可以的我们必须要求使用允许所需的权限。

只需将此代码添加到 mainActivity.java

Override
            public void onClick(View view) {
                // Request the permission
                ActivityCompat.requestPermissions(MainActivity.this,
                        new String[]{Manifest.permission.CAMERA},
                        PERMISSION_REQUEST_CAMERA);

替换 CAMERA 或根据需要添加 WRITE_EXTERNAL_STORAGE,并添加唯一代码。

                            new String[]{Manifest.permission.CAMERA,
Manifest.permission.WRITE_EXTERNAL_STORAGE},
                    101);

这是获取权限的简单代码。

我找到的最简单的方法是

private boolean checkPermissions(){
    if(ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
        return true;
    }
    else {
        ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_CODE);
        return false;
    }
}

将以下代码添加到 MainAcitivity 中的 OnCreate() 函数,它将弹出请求权限:

if (ActivityCompat.shouldShowRequestPermissionRationale(TestActivity.this,
                        Manifest.permission.WRITE_EXTERNAL_STORAGE)){
}
else {
     ActivityCompat.requestPermissions(TestActivity.this,
                            new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
                            100);
}
if (ActivityCompat.shouldShowRequestPermissionRationale(getActivity(),
                Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
            Log.d(TAG, "Permission granted");
        } else {
            ActivityCompat.requestPermissions(getActivity(),
                    new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
                    100);
        }

        fab.setOnClickListener(v -> {
            Bitmap b = BitmapFactory.decodeResource(getResources(), R.drawable.refer_pic);
            Intent share = new Intent(Intent.ACTION_SEND);
            share.setType("image/*");
            ByteArrayOutputStream bytes = new ByteArrayOutputStream();
            b.compress(Bitmap.CompressFormat.JPEG, 100, bytes);
            String path = MediaStore.Images.Media.insertImage(requireActivity().getContentResolver(),
                    b, "Title", null);
            Uri imageUri = Uri.parse(path);
            share.putExtra(Intent.EXTRA_STREAM, imageUri);
            share.putExtra(Intent.EXTRA_TEXT, "Here is text");
            startActivity(Intent.createChooser(share, "Share via"));
        });