根据这个:http://developer.android.com/preview/features/runtime-permissions.html#coding一个应用程序可以检查运行时权限和请求权限,如果它还没有被授予。弹出如下对话框:

如果用户拒绝一个重要的权限,在我看来,应用程序应该显示一个解释为什么需要权限和什么影响拒绝。该对话框有两个选项:

重试(再次请求许可) 拒绝(应用程序将工作没有该许可)。

但是,如果用户选中“Never ask again”,则不应该显示带有解释的第二个对话框,特别是如果用户之前已经拒绝了一次。 现在的问题是:我的应用程序如何知道用户是否选中了Never ask again?IMO onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)没有给我这个信息。

第二个问题是:谷歌是否计划在权限对话框中包含一个自定义消息,以解释为什么应用程序需要权限?这样就不会出现第二个对话框,这肯定会带来更好的用户体验。


当前回答

您可以通过检查权限原理是否显示在onRequestPermissionsResult()回调方法中来确定它。如果您发现任何权限集永远不会再次请求,您可以请求用户从设置中授予权限。

我的完整实现如下所示。它适用于单个或多个权限请求。使用以下或直接使用我的库。

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    if(permissions.length == 0){
        return;
    }
    boolean allPermissionsGranted = true;
    if(grantResults.length>0){
        for(int grantResult: grantResults){
            if(grantResult != PackageManager.PERMISSION_GRANTED){
                allPermissionsGranted = false;
                break;
            }
        }
    }
    if(!allPermissionsGranted){
        boolean somePermissionsForeverDenied = false;
        for(String permission: permissions){
            if(ActivityCompat.shouldShowRequestPermissionRationale(this, permission)){
                //denied
                Log.e("denied", permission);
            }else{
                if(ActivityCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED){
                    //allowed
                    Log.e("allowed", permission);
                } else{
                    //set to never ask again
                    Log.e("set to never ask again", permission);
                    somePermissionsForeverDenied = true;
                }
            }
        }
        if(somePermissionsForeverDenied){
            final AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this);
            alertDialogBuilder.setTitle("Permissions Required")
                    .setMessage("You have forcefully denied some of the required permissions " +
                            "for this action. Please open settings, go to permissions and allow them.")
                    .setPositiveButton("Settings", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
                                    Uri.fromParts("package", getPackageName(), null));
                            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                            startActivity(intent);
                        }
                    })
                    .setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                        }
                    })
                    .setCancelable(false)
                    .create()
                    .show();
        }
    } else {
        switch (requestCode) {
            //act according to the request code used while requesting the permission(s).
        }
    }
}

其他回答

试试这个简单的权限库。它将在3个简单的步骤中处理与权限相关的所有操作。这节省了我的时间。您可以在15分钟内完成所有权限相关的工作。

它可以处理拒绝,它可以处理永不再请求,它可以调用应用程序设置的权限,它可以给出一个Rational消息,它可以给出一个拒绝消息,它可以给出一个接受的权限列表,它可以给出一个拒绝的权限列表,等等。

https://github.com/ParkSangGwon/TedPermission

步骤1:添加依赖项

dependencies {
     compile 'gun0912.ted:tedpermission:2.1.1'
     //check the above link for latest libraries
}

步骤2:征求许可

TedPermission.with(this)
    .setPermissionListener(permissionlistener)
    .setDeniedMessage("If you reject permission,you can not use this service\n\nPlease turn on permissions at [Setting] > [Permission]")
    .setPermissions(Manifest.permission.READ_CONTACTS, Manifest.permission.ACCESS_FINE_LOCATION)
    .check();

步骤3:处理权限响应

PermissionListener permissionlistener = new PermissionListener() {
    @Override
    public void onPermissionGranted() {
        Toast.makeText(MainActivity.this, "Permission Granted", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onPermissionDenied(ArrayList<String> deniedPermissions) {
        Toast.makeText(MainActivity.this, "Permission Denied\n" + deniedPermissions.toString(), Toast.LENGTH_SHORT).show();
    }
};

一个有用的函数来确定任意权限是否被阻止请求(在Kotlin中):

private fun isPermissionBlockedFromAsking(activity: Activity, permission: String): Boolean {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        return ContextCompat.checkSelfPermission(activity, permission) != PackageManager.PERMISSION_GRANTED
            && !activity.shouldShowRequestPermissionRationale(permission)
            && PreferenceManager.getDefaultSharedPreferences(activity).getBoolean(permission, false)
    }
    return false
}

当你第一次请求一个权限时,需要设置一个共享首选项布尔值为true,其中包含你想要的权限名称(例如android.Manifest.permission.READ_PHONE_STATE)。


解释:

Build.VERSION。SDK_INT >= Build.VERSION_CODES。因为有些代码只能在API级别23+上运行。

ContextCompat。checkSelfPermission(activity, permission) != PackageManager。permission_granting检查我们还没有权限。

activity.shouldShowRequestPermissionRationale(permission)来检查用户是否再次拒绝了应用程序的请求。由于这个函数的特性,还需要下面这行代码。

PreferenceManager.getDefaultSharedPreferences(活动)。getBoolean(permission, false)用于区分“Never asked”和“Never ask again”状态(以及在第一次权限请求时将值设置为true),因为前一行不会返回此信息。

开发者预览2对应用程序请求权限的方式进行了一些更改(参见http://developer.android.com/preview/support.html#preview2-notes)。

第一个对话框现在看起来像这样:

没有“永不再次显示”复选框(不像开发人员预览1)。如果用户拒绝权限,如果权限是必不可少的应用程序,它可以显示另一个对话框来解释应用程序请求该权限的原因,例如:

如果用户再次拒绝,应用程序应该关闭(如果它绝对需要该权限)或继续运行有限的功能。如果用户重新考虑(并选择重试),则再次请求权限。这次的提示符是这样的:

第二次显示“永不再问”复选框。如果用户再次拒绝并且勾选复选框,就不会再发生任何事情。 是否勾选复选框可以通过使用Activity.shouldShowRequestPermissionRationale(String)来确定,例如:

if (shouldShowRequestPermissionRationale(Manifest.permission.WRITE_CONTACTS)) {...

这就是Android文档所说的(https://developer.android.com/training/permissions/requesting.html):

To help find the situations where you need to provide extra explanation, the system provides the Activity.shouldShowRequestPermissionRationale(String) method. This method returns true if the app has requested this permission previously and the user denied the request. That indicates that you should probably explain to the user why you need the permission. If the user turned down the permission request in the past and chose the Don't ask again option in the permission request system dialog, this method returns false. The method also returns false if the device policy prohibits the app from having that permission.

要知道用户是否拒绝了“never ask again”,你可以再次检查你的onRequestPermissionsResult中的shouldShowRequestPermissionRationale方法,当用户没有授予权限时。

@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    if (requestCode == REQUEST_PERMISSION) {
        // for each permission check if the user granted/denied them
        // you may want to group the rationale in a single dialog,
        // this is just an example
        for (int i = 0, len = permissions.length; i < len; i++) {
            String permission = permissions[i];
            if (grantResults[i] == PackageManager.PERMISSION_DENIED) {
            // user rejected the permission
                boolean showRationale = shouldShowRequestPermissionRationale( permission );
                if (! showRationale) {
                    // user also CHECKED "never ask again"
                    // you can either enable some fall back,
                    // disable features of your app
                    // or open another dialog explaining
                    // again the permission and directing to
                    // the app setting
                } else if (Manifest.permission.WRITE_CONTACTS.equals(permission)) {
                    showRationale(permission, R.string.permission_denied_contacts);
                    // user did NOT check "never ask again"
                    // this is a good place to explain the user
                    // why you need the permission and ask if he wants
                    // to accept it (the rationale)
                } else if ( /* possibly check more permissions...*/ ) {
                }
            }
        }
    }
}

你可以用下面的代码打开你的应用程序设置:

Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package", getPackageName(), null);
intent.setData(uri);
startActivityForResult(intent, REQUEST_PERMISSION_SETTING);

无法将用户直接发送到授权页面。

相反,你会收到回调onRequestPermissionsResult()作为PERMISSION_DENIED当你再次请求权限时,而落在shouldShowRequestPermissionRationale()的错误条件下

来自Android文档:

When the system asks the user to grant a permission, the user has the option of telling the system not to ask for that permission again. In that case, any time an app uses requestPermissions() to ask for that permission again, the system immediately denies the request. The system calls your onRequestPermissionsResult() callback method and passes PERMISSION_DENIED, the same way it would if the user had explicitly rejected your request again. This means that when you call requestPermissions(), you cannot assume that any direct interaction with the user has taken place.

您可以使用if (ActivityCompat。shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA)方法来检测never ask是否被检查。

更多参考:检查这个

检查是否使用了多个权限:

  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.
                        }

解释()方法

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

上面的代码还将显示对话框,这将重定向到用户的应用程序设置屏幕,在那里他可以给予权限,如果选中了“永不再问”按钮。