我正在翻阅关于Android m中的新权限模型的官方文档。它讨论了shouldShowRequestPermissionRationale()函数,如果应用程序之前请求了此权限,并且用户拒绝了请求,该函数将返回true。如果用户在过去拒绝了权限请求并选择Don't ask again选项,则此方法返回false。

但是我们如何区分以下两种情况呢?

案例1:应用程序没有权限,之前也没有向用户请求过权限。在这种情况下,shouldShowRequestPermissionRationale()将返回false,因为这是我们第一次请求用户。

情况2:用户拒绝了权限并选择了“不要再问了”,在这种情况下shouldShowRequestPermissionRationale()将返回false。

我想把用户发送到案例2中的应用设置页面。我怎么微分这两种情况呢?


当前回答

我也遇到过同样的问题,但我解决了。为了简化工作,我编写了一个util类来处理运行时权限。

public class PermissionUtil {
    /*
    * Check if version is marshmallow and above.
    * Used in deciding to ask runtime permission
    * */
    public static boolean shouldAskPermission() {
        return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M);
    }
private static boolean shouldAskPermission(Context context, String permission){
        if (shouldAskPermission()) {
            int permissionResult = ActivityCompat.checkSelfPermission(context, permission);
            if (permissionResult != PackageManager.PERMISSION_GRANTED) {
                return true;
            }
        }
        return false;
    }
public static void checkPermission(Context context, String permission, PermissionAskListener listener){
/*
        * If permission is not granted
        * */
        if (shouldAskPermission(context, permission)){
/*
            * If permission denied previously
            * */
            if (((Activity) context).shouldShowRequestPermissionRationale(permission)) {
                listener.onPermissionPreviouslyDenied();
            } else {
                /*
                * Permission denied or first time requested
                * */
if (PreferencesUtil.isFirstTimeAskingPermission(context, permission)) {
                    PreferencesUtil.firstTimeAskingPermission(context, permission, false);
                    listener.onPermissionAsk();
                } else {
                    /*
                    * Handle the feature without permission or ask user to manually allow permission
                    * */
                    listener.onPermissionDisabled();
                }
            }
        } else {
            listener.onPermissionGranted();
        }
    }
/*
    * Callback on various cases on checking permission
    *
    * 1.  Below M, runtime permission not needed. In that case onPermissionGranted() would be called.
    *     If permission is already granted, onPermissionGranted() would be called.
    *
    * 2.  Above M, if the permission is being asked first time onPermissionAsk() would be called.
    *
    * 3.  Above M, if the permission is previously asked but not granted, onPermissionPreviouslyDenied()
    *     would be called.
    *
    * 4.  Above M, if the permission is disabled by device policy or the user checked "Never ask again"
    *     check box on previous request permission, onPermissionDisabled() would be called.
    * */
    public interface PermissionAskListener {
/*
        * Callback to ask permission
        * */
        void onPermissionAsk();
/*
        * Callback on permission denied
        * */
        void onPermissionPreviouslyDenied();
/*
        * Callback on permission "Never show again" checked and denied
        * */
        void onPermissionDisabled();
/*
        * Callback on permission granted
        * */
        void onPermissionGranted();
    }
}

PreferenceUtil方法如下。

public static void firstTimeAskingPermission(Context context, String permission, boolean isFirstTime){
SharedPreferences sharedPreference = context.getSharedPreferences(PREFS_FILE_NAME, MODE_PRIVATE;
 sharedPreference.edit().putBoolean(permission, isFirstTime).apply();
 }
public static boolean isFirstTimeAskingPermission(Context context, String permission){
return context.getSharedPreferences(PREFS_FILE_NAME, MODE_PRIVATE).getBoolean(permission, true);
}

现在,您所需要的就是使用checkPermission方法和适当的参数。

举个例子,

PermissionUtil.checkPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE,
                    new PermissionUtil.PermissionAskListener() {
                        @Override
                        public void onPermissionAsk() {
                            ActivityCompat.requestPermissions(
                                    thisActivity,
              new String[]{Manifest.permission.READ_CONTACTS},
                            REQUEST_EXTERNAL_STORAGE
                            );
                        }
@Override
                        public void onPermissionPreviouslyDenied() {
                       //show a dialog explaining permission and then request permission
                        }
@Override
                        public void onPermissionDisabled() {
Toast.makeText(context, "Permission Disabled.", Toast.LENGTH_SHORT).show();
                        }
@Override
                        public void onPermissionGranted() {
                            readContacts();
                        }
                    });

Case 1: The app doesn't have a permission and the user has not been asked for the permission before. In this case, shouldShowRequestPermissionRationale() will return false because this is the first time we're asking the user. Case 2: The user has denied the permission and selected "Don't ask again", in this case too shouldShowRequestPermissionRationale() will return false. I would want to send the user to the App's settings page in Case 2. How do i go about differentiating these two cases?

你会得到回调onPermissionAsk的情况1,和onPermissionDisabled的情况2。

快乐编码:)

其他回答

我理解它的方式,shouldShowRequestPermissionRationale()在底层运行许多用例,并通知应用程序是否显示正在请求的权限的解释。

运行时权限背后的思想是,大多数情况下,用户将对权限请求说Yes。这样用户就只需要点击一次。当然,这个请求应该在正确的上下文中使用——即在按下“摄像”按钮时请求摄像许可。

If the user denies the request, but after some time comes around and presses the "Camera" button again, shouldShowRequestPermissionRationale() will return true, so the app can show some meaningful explanation why the permission is requested, and why the app won't work properly without it. Normally you would show in that dialog window a button to deny again/decide later, and a button to grant the permissions. The grant permissions button in the rationale dialog, should start the permission request again. This time the user will also have a "Never show again" checkbox. Should he decide to select it, and deny the permission again, it would notify the Android system that the user and the app are not on the same page. That action would have two consequences - shouldShowRequestPermissionRationale() will always return false, and the requestPermissions() method will not show any dialog, but will directly return denied to the onRequestPermissionsResult callback.

但是还有另一种可能的场景,可以使用onRequestPermissionsResult。例如,一些设备可能有禁用摄像头的设备策略(为CIA、DARPA等工作)。在这些设备上,onRequestPermissionsResult将总是返回false,并且requestPermissions()方法将无声地拒绝请求。

这是我从Ben Poiesz (Android框架的产品经理)的播客中了解到的。 http://androidbackstage.blogspot.jp/2015/08/episode-33-permission-mission.html

“M Preview 1”后,如果第一次弹出对话框,则没有“Never ask again”复选框。

如果用户拒绝权限请求,第二次请求权限时,权限对话框中会出现“Never ask again”复选框。

所以逻辑应该是这样的:

Request permission: if (ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(context, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_CODE); } else { //Do the stuff that requires permission... } Check if the permission was denied or granted in onRequestPermissionsResult. If the permission was denied previously, this time there will be a Never ask again checkbox in the permission dialog. Call shouldShowRequestPermissionRationale to see if the user checked Never ask again. shouldShowRequestPermissionRationale method returns false only if the user selected Never ask again or device policy prohibits the app from having that permission: if (grantResults.length > 0){ if(grantResults[0] == PackageManager.PERMISSION_GRANTED) { //Do the stuff that requires permission... }else if (grantResults[0] == PackageManager.PERMISSION_DENIED){ // Should we show an explanation? if (ActivityCompat.shouldShowRequestPermissionRationale(context, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { //Show permission explanation dialog... }else{ //Never ask again selected, or device policy prohibits the app from having that permission. //So, disable that feature, or fall back to another situation... } } }

因此,如果用户勾选了“永不再问”,您将不必跟踪。

我也遇到过同样的问题,但我解决了。为了简化工作,我编写了一个util类来处理运行时权限。

public class PermissionUtil {
    /*
    * Check if version is marshmallow and above.
    * Used in deciding to ask runtime permission
    * */
    public static boolean shouldAskPermission() {
        return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M);
    }
private static boolean shouldAskPermission(Context context, String permission){
        if (shouldAskPermission()) {
            int permissionResult = ActivityCompat.checkSelfPermission(context, permission);
            if (permissionResult != PackageManager.PERMISSION_GRANTED) {
                return true;
            }
        }
        return false;
    }
public static void checkPermission(Context context, String permission, PermissionAskListener listener){
/*
        * If permission is not granted
        * */
        if (shouldAskPermission(context, permission)){
/*
            * If permission denied previously
            * */
            if (((Activity) context).shouldShowRequestPermissionRationale(permission)) {
                listener.onPermissionPreviouslyDenied();
            } else {
                /*
                * Permission denied or first time requested
                * */
if (PreferencesUtil.isFirstTimeAskingPermission(context, permission)) {
                    PreferencesUtil.firstTimeAskingPermission(context, permission, false);
                    listener.onPermissionAsk();
                } else {
                    /*
                    * Handle the feature without permission or ask user to manually allow permission
                    * */
                    listener.onPermissionDisabled();
                }
            }
        } else {
            listener.onPermissionGranted();
        }
    }
/*
    * Callback on various cases on checking permission
    *
    * 1.  Below M, runtime permission not needed. In that case onPermissionGranted() would be called.
    *     If permission is already granted, onPermissionGranted() would be called.
    *
    * 2.  Above M, if the permission is being asked first time onPermissionAsk() would be called.
    *
    * 3.  Above M, if the permission is previously asked but not granted, onPermissionPreviouslyDenied()
    *     would be called.
    *
    * 4.  Above M, if the permission is disabled by device policy or the user checked "Never ask again"
    *     check box on previous request permission, onPermissionDisabled() would be called.
    * */
    public interface PermissionAskListener {
/*
        * Callback to ask permission
        * */
        void onPermissionAsk();
/*
        * Callback on permission denied
        * */
        void onPermissionPreviouslyDenied();
/*
        * Callback on permission "Never show again" checked and denied
        * */
        void onPermissionDisabled();
/*
        * Callback on permission granted
        * */
        void onPermissionGranted();
    }
}

PreferenceUtil方法如下。

public static void firstTimeAskingPermission(Context context, String permission, boolean isFirstTime){
SharedPreferences sharedPreference = context.getSharedPreferences(PREFS_FILE_NAME, MODE_PRIVATE;
 sharedPreference.edit().putBoolean(permission, isFirstTime).apply();
 }
public static boolean isFirstTimeAskingPermission(Context context, String permission){
return context.getSharedPreferences(PREFS_FILE_NAME, MODE_PRIVATE).getBoolean(permission, true);
}

现在,您所需要的就是使用checkPermission方法和适当的参数。

举个例子,

PermissionUtil.checkPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE,
                    new PermissionUtil.PermissionAskListener() {
                        @Override
                        public void onPermissionAsk() {
                            ActivityCompat.requestPermissions(
                                    thisActivity,
              new String[]{Manifest.permission.READ_CONTACTS},
                            REQUEST_EXTERNAL_STORAGE
                            );
                        }
@Override
                        public void onPermissionPreviouslyDenied() {
                       //show a dialog explaining permission and then request permission
                        }
@Override
                        public void onPermissionDisabled() {
Toast.makeText(context, "Permission Disabled.", Toast.LENGTH_SHORT).show();
                        }
@Override
                        public void onPermissionGranted() {
                            readContacts();
                        }
                    });

Case 1: The app doesn't have a permission and the user has not been asked for the permission before. In this case, shouldShowRequestPermissionRationale() will return false because this is the first time we're asking the user. Case 2: The user has denied the permission and selected "Don't ask again", in this case too shouldShowRequestPermissionRationale() will return false. I would want to send the user to the App's settings page in Case 2. How do i go about differentiating these two cases?

你会得到回调onPermissionAsk的情况1,和onPermissionDisabled的情况2。

快乐编码:)

更新

我相信下面CanC的答案是正确的,应该遵循。唯一确定的方法是在onRequestPermissionResult回调中使用shouldShowPermissionRationale来验证这一点。

==

我最初的回答是:

我发现的唯一方法是自己跟踪这是否是第一次(例如使用共享的偏好)。如果不是第一次,则使用shouldShowRequestPermissionRationale()进行区分。

另见:Android M -检查运行时权限-如何确定用户是否勾选了“永不再问”?

如果有人对Kotlin解决方案感兴趣,我将@muthuraj的答案重构为Kotlin。它也现代化了一点,有一个完成块,而不是监听器。

PermissionUtil

object PermissionUtil {
    private val PREFS_FILE_NAME = "preference"

    fun firstTimeAskingPermission(context: Context, permission: String, isFirstTime: Boolean) {
        val sharedPreference = context.getSharedPreferences(PREFS_FILE_NAME, MODE_PRIVATE)
        sharedPreference.preferences.edit().putBoolean(permission,
                isFirstTime).apply()
    }

    fun isFirstTimeAskingPermission(context: Context, permission: String): Boolean {
        val sharedPreference = context.getSharedPreferences(PREFS_FILE_NAME, MODE_PRIVATE)
        return sharedPreference.preferences.getBoolean(permission,
                true)
    }
}

PermissionHandler

enum class CheckPermissionResult {
    PermissionAsk,
    PermissionPreviouslyDenied,
    PermissionDisabled,
    PermissionGranted
}

typealias PermissionCheckCompletion = (CheckPermissionResult) -> Unit


object PermissionHandler {

    private fun shouldAskPermission(context: Context, permission: String): Boolean {
        return ContextCompat.checkSelfPermission(context,
                permission) != PackageManager.PERMISSION_GRANTED
    }

    fun checkPermission(context: Context, permission: String, completion: PermissionCheckCompletion) {
        // If permission is not granted
        if (shouldAskPermission(context, permission)) {
            //If permission denied previously
            if ((context as Activity).shouldShowRequestPermissionRationale(permission)) {
                completion(CheckPermissionResult.PermissionPreviouslyDenied)
            } else {
                // Permission denied or first time requested
                if (PermissionUtil.isFirstTimeAskingPermission(context,
                                permission)) {
                    PermissionUtil.firstTimeAskingPermission(context,
                            permission,
                            false)
                    completion(CheckPermissionResult.PermissionAsk)
                } else {
                    // Handle the feature without permission or ask user to manually allow permission
                    completion(CheckPermissionResult.PermissionDisabled)
                }
            }
        } else {
            completion(CheckPermissionResult.PermissionGranted)
        }
    }
}

实现

PermissionHandler.checkPermission(activity,
                    Manifest.permission.CAMERA) { result ->
                when (result) {
                    CheckPermissionResult.PermissionGranted -> {
                        // openCamera()
                    }
                    CheckPermissionResult.PermissionDisabled -> {
                        // displayAlert(noPermissionAlert)
                    }
                    CheckPermissionResult.PermissionAsk -> {
                        // requestCameraPermissions()
                    }
                    CheckPermissionResult.PermissionPreviouslyDenied -> {
                        // displayAlert(permissionRequestAlert)
                    }
                }
            }