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

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

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

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

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


当前回答

shouldShowRequestPermissionRationale()方法可以用来检查用户是否选择了“never asked again”选项并拒绝了权限。 有很多代码示例,所以我宁愿解释如何将它用于这样的目的,因为我认为它的名称和实现使它比实际情况更加复杂。

正如在运行时请求权限中解释的那样,如果选项“never ask again”可见,该方法返回true,否则返回false;因此,它在第一次显示对话框时返回false,然后从第二次开始返回true,只有当用户拒绝选择该选项的权限时,它才会再次返回false。

要检测这种情况,您可以检测序列false-true-false,或者(更简单)您可以使用一个标记来跟踪对话框显示的初始时间。在此之后,该方法返回true或false,其中false将允许您检测何时选择该选项。

其他回答

扩展上面mVck的答案,下面的逻辑确定是否“Never ask again”已经为给定的权限请求进行了检查:

bool bStorage = grantResults[0] == Permission.Granted;
bool bNeverAskForStorage =
    !bStorage && (
        _bStorageRationaleBefore == true  && _bStorageRationaleAfter == false ||
        _bStorageRationaleBefore == false && _bStorageRationaleAfter == false
    );

摘自下面(完整的例子见这个答案)

private bool _bStorageRationaleBefore;
private bool _bStorageRationaleAfter;        
private const int ANDROID_PERMISSION_REQUEST_CODE__SDCARD = 2;
//private const int ANDROID_PERMISSION_REQUEST_CODE__CAMERA = 1;
private const int ANDROID_PERMISSION_REQUEST_CODE__NONE = 0;

public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Permission[] grantResults)
{
    base.OnRequestPermissionsResult(requestCode, permissions, grantResults);

    switch (requestCode)
    {
        case ANDROID_PERMISSION_REQUEST_CODE__SDCARD:               
            _bStorageRationaleAfter = ShouldShowRequestPermissionRationale(Android.Manifest.Permission.WriteExternalStorage);
            bool bStorage = grantResults[0] == Permission.Granted;
            bool bNeverAskForStorage =
                !bStorage && (
                    _bStorageRationaleBefore == true  && _bStorageRationaleAfter == false ||
                    _bStorageRationaleBefore == false && _bStorageRationaleAfter == false
                );      
            break;                
    }
}

private List<string> GetRequiredPermissions(out int requestCode)
{
    // Android v6 requires explicit permission granting from user at runtime for security reasons            
    requestCode = ANDROID_PERMISSION_REQUEST_CODE__NONE; // 0
    List<string> requiredPermissions = new List<string>();

    _bStorageRationaleBefore = ShouldShowRequestPermissionRationale(Android.Manifest.Permission.WriteExternalStorage);
    Permission writeExternalStoragePerm = ApplicationContext.CheckSelfPermission(Android.Manifest.Permission.WriteExternalStorage);
    //if(extStoragePerm == Permission.Denied)
    if (writeExternalStoragePerm != Permission.Granted)
    {
        requestCode |= ANDROID_PERMISSION_REQUEST_CODE__SDCARD;
        requiredPermissions.Add(Android.Manifest.Permission.WriteExternalStorage);
    }

    return requiredPermissions;
}

protected override void OnCreate(Bundle savedInstanceState)
{
    base.OnCreate(savedInstanceState);

        // Android v6 requires explicit permission granting from user at runtime for security reasons
        int requestCode;
        List<string> requiredPermissions = GetRequiredPermissions(out requestCode);
        if (requiredPermissions != null && requiredPermissions.Count > 0)
        {
            if (requestCode >= ANDROID_PERMISSION_REQUEST_CODE__SDCARD)                    
            {
                _savedInstanceState = savedInstanceState;
                RequestPermissions(requiredPermissions.ToArray(), requestCode);
                return;
            }
        }
    }            

    OnCreate2(savedInstanceState);
}

这里有一个很好的和简单的方法来检查当前的权限状态:

    @Retention(RetentionPolicy.SOURCE)
    @IntDef({GRANTED, DENIED, BLOCKED_OR_NEVER_ASKED })
    public @interface PermissionStatus {}

    public static final int GRANTED = 0;
    public static final int DENIED = 1;
    public static final int BLOCKED_OR_NEVER_ASKED = 2;

    @PermissionStatus 
    public static int getPermissionStatus(Activity activity, String androidPermissionName) {
        if(ContextCompat.checkSelfPermission(activity, androidPermissionName) != PackageManager.PERMISSION_GRANTED) {
            if(!ActivityCompat.shouldShowRequestPermissionRationale(activity, androidPermissionName)){
                return BLOCKED_OR_NEVER_ASKED;
            }
            return DENIED;
        }
        return GRANTED;
    }

警告:在用户通过用户提示接受/拒绝权限之前,在第一个应用程序启动时返回BLOCKED_OR_NEVER_ASKED(在sdk 23+设备上)

更新:

Android支持库现在似乎也有一个非常类似的类Android .support.v4.content。PermissionChecker,包含checkSelfPermission(),返回:

public static final int PERMISSION_GRANTED = 0;
public static final int PERMISSION_DENIED = -1;
public static final int PERMISSION_DENIED_APP_OP = -2;

在阅读了一些答案后,我发现了许多冗长而令人困惑的答案 我的结论是

if (!ActivityCompat.shouldShowRequestPermissionRationale(this,Manifest.permission.READ_EXTERNAL_STORAGE))
                Toast.makeText(this, "permanently denied", Toast.LENGTH_SHORT).show();

OnRequestPermissionResult-free和shouldshowrequestpermissionrationalfree方法:

public static void requestDangerousPermission(AppCompatActivity activity, String permission) {
        if (hasPermission(activity, permission)) return;
        requestPermission();

        new Handler().postDelayed(() -> {
            if (activity.getLifecycle().getCurrentState() == Lifecycle.State.RESUMED) {
                Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                intent.setData(Uri.parse("package:" + context.getPackageName()));
                context.startActivity(intent);
            }
        }, 250);
    }

如果没有权限弹出,250ms后打开设备设置(如果选择了“Never ask again”,就是这种情况)。

开发者预览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);

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