Android M Permissions:对shouldShowRequestPermissionRationale()函数的使用感到困惑

aks*_*t23 137 permissions android android-6.0-marshmallow

我正在阅读关于Android M中新Permissions模型的官方文档.它讨论了如果应用程序先前已请求此权限并且用户拒绝该请求shouldShowRequestPermissionRationale(),则返回的函数true.如果用户过去拒绝了权限请求并选择了"不再询问"选项,则此方法返回false.

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

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

情况2:用户已拒绝该权限并选择"不再询问",在这种情况下,shouldInShowRequestPermissionRationale()也将返回false.

我想将用户发送到案例2中的应用程序设置页面.我如何区分这两种情况?

Can*_*anC 162

在M预览1之后,如果第一次显示该对话框,则没有从不再询问复选框.

如果用户拒绝权限请求,则在第二次请求权限时,权限对话框中将出现" 永不再询问"复选框.

所以逻辑应该是这样的:

  1. 请求许可:

    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...
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 检查权限是否被拒绝或授予onRequestPermissionsResult.

    如果先前已拒绝该权限,则此时将在权限对话框中显示" 永不再询问"复选框.

    打电话询问shouldShowRequestPermissionRationale用户是否选中了" 永不再问".shouldShowRequestPermissionRationale只有当用户选择" 永不再询问"或设备策略禁止该应用拥有该权限时,该方法才会返回false :

    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...
            }
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)

因此,您无需跟踪用户是否选中了" 永不再询问".

  • 澄清一点,如果用户从未被要求获得权限(即第一次运行应用程序),那么shouldShowRequestPermissionRationale()也将返回false.如果您遵循所提供示例的逻辑,则不会遇到这种情况.但是,2下的措辞有点误导. (41认同)
  • 我不确定,这似乎有缺陷.我们怎么知道这是第一次被问到用户?我必须跟踪用户是否被问到,如果他这样做了,那么我必须改变逻辑.对我没有任何意义. (11认同)
  • 我认为这是谷歌的一大失败。官方文档指出应该在检查权限之前调用 shouldShowRequestPermissionRationale()(参见 https://developer.android.com/training/permissions/requesting#explain),但是 StackOverflow 中的所有答案都在 onRequestPermissionResult() 中依次调用它来区分用户是否点击了“不再询问”。 (6认同)
  • 这个android逻辑是如此愚蠢!它迫使我在回调中调用`should`并在NVM中保存其计数器值,以便知道我是否需要在下次应用程序打开时再次提示请求!...哇(facepalm)...只有一个调用返回状态枚举太难了吗? (5认同)
  • 我认为值得注意的是,在`ActivityCompat.shouldShowRequestPermissionRationale(...)`中传递`context`的地方,参数实际上是`Activity`类型.可能不会影响到你,但在我的情况下确实如此. (4认同)

mut*_*raj 22

我有同样的问题,我想出来了.为了简化生活,我编写了一个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();
    }
}
Run Code Online (Sandbox Code Playgroud)

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);
}
Run Code Online (Sandbox Code Playgroud)

现在,您只需要使用带有适当参数的方法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();
                        }
                    });
Run Code Online (Sandbox Code Playgroud)

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

情况2:用户已拒绝该权限并选择"不再询问",在这种情况下,shouldInShowRequestPermissionRationale()也将返回false.

我想将用户发送到案例2中的应用程序设置页面.我如何区分这两种情况?

你会得到回调的onPermissionAsk为1的情况下,和onPermissionDisabled的情况下2.

快乐编码:)


Ale*_*scu 8

UPDATE

我相信CanC的答案是正确的应该遵循.唯一可以确定的方法是使用shouldShowPermissionRationale在onRequestPermissionResult回调中验证这一点.

==

我的原始答案:

我找到的唯一方法是自己跟踪这是否是第一次(例如使用共享首选项).如果不是第一次,那就用吧shouldShowRequestPermissionRationale()用来区分.

另请参阅:Android M - 检查运行时权限 - 如何确定用户是否选中"从不再询问"?


Shu*_*app 6

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

运行时权限背后的想法是,大多数情况下,用户会对权限请求说"是".这样用户只需点击一下即可.当然,请求应该在正确的上下文中使用 - 即在按下"Camera"按钮时要求Camera权限.

如果用户拒绝请求,但在一段时间后再次按下"Camera"按钮,shouldShowRequestPermissionRationale()将返回true,因此应用程序可以显示一些有意义的解释,为什么请求权限,以及为什么应用程序不会没有它就能正常工作.通常,您会在该对话框窗口中显示一个按钮,以便稍后拒绝/决定,以及一个授予权限的按钮.基本原理对话框中的授予权限按钮应该再次启动权限请求.这次用户还将出现"Never again again"复选框.如果他决定选择它并再次拒绝许可,它会通知Android系统用户和应用程序不在同一页面上.该操作会产生两种结果 - shouldShowRequestPermissionRationale()将始终返回false,而requestPermissions()方法将不会显示任何对话框,但会直接返回拒绝onRequestPermissionsResult回调.

但是还有另一种可能使用onRequestPermissionsResult的情况.例如,某些设备可能具有禁用摄像头的设备策略(适用于CIA,DARPA等).在这些设备上,onRequestPermissionsResult将始终返回false,而requestPermissions()方法将以静默方式拒绝该请求.

这就是我通过与Ben Poiesz(Android框架上的产品经理)一起收听播客而收集到的内容.
http://androidbackstage.blogspot.jp/2015/08/episode-33-permission-mission.html


Wei*_*ANG 6

如果有人愿意,可以发布另一个选项.您可以使用Google自己提供的EasyPermissions,如"简化Android M系统权限".

那你就不必shouldShowRequestPermissionRationale直接处理了.

  • EasyPermissions 的问题几乎保持不变。在内部询问“permissionPermanentlyDenied”只会调用“shouldShowPermissionsRationale”,并在从未请求用户授予权限的情况下返回“true”。 (2认同)