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

我正在浏览关于Android M中新权限模型的官方文档。它讲述了shouldShowRequestPermissionRationale()函数,如果应用程序以前请求了此权限并且用户拒绝了请求,则返回true 。 如果用户过去拒绝了权限请求,并select了不要再次请求选项,则此方法返回false

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

案例1 :该应用程序没有权限,用户之前没有被要求获得许可。 在这种情况下,shouldShowRequestPermissionRationale()将返回false,因为这是我们第一次询问用户。

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

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

M Preview 1之后,如果第一次显示对话框,则不会再询问checkbox。

如果用户拒绝了权限请求,则第二次请求权限时会在权限对话框中出现一个“ 从不询问”checkbox。

所以逻辑应该是这样的:

  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... } 
  2. 检查权限是否被拒绝或授予onRequestPermissionsResult

    如果权限之前被拒绝,这次在权限对话框中将会出现一个永不问问的checkbox。

    调用shouldShowRequestPermissionRationale来查看用户是否被选中。 shouldShowRequestPermissionRationale方法只有在用户select永不再问或设备策略禁止应用程序拥有该权限时才返回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... } } } 

所以,你不必跟踪用户是否检查过。 不要再次询问

我有同样的问题,我想清楚了。 为了使生活更简单,我编写了一个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 (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(); } }); 

案例1:该应用程序没有权限,用户之前没有被要求获得许可。 在这种情况下,shouldShowRequestPermissionRationale()将返回false,因为这是我们第一次询问用户。

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

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

对于情况1, onPermissionAsk将得到callback,对于情况2,将得到onPermissionDisabled

快乐编码:)

UPDATE

我相信CanC的回答是正确的,应该遵循。 唯一可以肯定的方法是使用shouldShowPermissionRationale在onRequestPermissionResultcallback中validation。

==

我原来的答案是:

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

另请参阅: Android M – 检查运行时权限 – 如何确定用户是否选中“永远不要再询问”?

我的理解是,shouldShowRequestPermissionRationale()在底层运行一些用例,并通知应用程序是否显示请求权限的解释。

运行时权限背后的想法是大多数时候,用户会对权限请求说“是”。 这样用户将不得不只做一次点击。 当然,这个请求应该在正确的上下文中使用 – 例如当按下“Camera”button时请求相机的权限。

如果用户拒绝了请求,但是经过一段时间后再次按下“Camera”button,shouldShowRequestPermissionRationale()将返回true,所以应用程序可以显示一些有意义的解释为什么请求权限,以及为什么应用程序不会没有它就能正常工作。 通常情况下,你会在对话框窗口中显示一个button来拒绝/稍后决定,还有一个button来授予权限。 基本对话框中的授权许可button应该再次启动许可请求。 这次用户也将有一个“从不再显示”checkbox。 如果他决定select它,并再次拒绝该权限,则会通知Android系统用户和应用程序不在同一页面上。 这个动作会有两个结果 – shouldShowRequestPermissionRationale()将总是返回false,requestPermissions()方法不会显示任何对话框,但会直接返回拒绝给onRequestPermissionsResultcallback。

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

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

只要张贴另一个选项,如果有人可能会觉得。 您可以使用Google自己提供的EasyPermissions ,如“简化Android M系统权限”所述。

那么你不必直接处理shouldShowRequestPermissionRationale

可能对某人有用: –

我注意到的是,如果我们检查onRequestPermissionsResult()callback方法中的shouldShowRequestPermissionRationale()标志,它只显示两种状态。

状态1:返回true: – 任何时候用户点击拒绝权限(包括第一次)。

状态2:拒绝 – 如果用户select“不再询问”。

详细的工作示例的链接 。

检查这个实现。 对我来说工作很好。 基本上你可以在checkPermissions()方法中检查权限列表。 您检查onRequestPermissionsResult()的权限请求的结果。 当用户select“不再询问”时,实现让你解决这两种情况。 在这个实现中,如果select“再也不要问”,对话框有一个选项可以把他带到应用程序设置活动。

所有这些代码是在我的片段。 我想这会更好地创build一个专门的类来做到这一点,就像一个PermissionManager,但我不确定。

 /** * responsible for checking if permissions are granted. In case permissions are not granted, the user will be requested and the method returns false. In case we have all permissions, the method return true. * The response of the request for the permissions is going to be handled in the onRequestPermissionsResult() method * @param permissions list of permissions to be checked if are granted onRequestPermissionsResult(). * @param requestCode request code to identify this request in * @return true case we already have all permissions. false in case we had to prompt the user for it. */ private boolean checkPermissions(List<String> permissions, int requestCode) { List<String> permissionsNotGranted = new ArrayList<>(); for (String permission : permissions) { if (ContextCompat.checkSelfPermission(getActivity(), permission) != PackageManager.PERMISSION_GRANTED) permissionsNotGranted.add(permission); } //If there is any permission we don't have (it's going to be in permissionsNotGranted List) , we need to request. if (!permissionsNotGranted.isEmpty()) { requestPermissions(permissionsNotGranted.toArray(new String[permissionsNotGranted.size()]), requestCode); return false; } return true; } /** * called after permissions are requested to the user. This is called always, either * has granted or not the permissions. * @param requestCode int code used to identify the request made. Was passed as parameter in the * requestPermissions() call. * @param permissions Array containing the permissions asked to the user. * @param grantResults Array containing the results of the permissions requested to the user. */ @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { switch (requestCode) { case YOUR_REQUEST_CODE: { boolean anyPermissionDenied = false; boolean neverAskAgainSelected = false; // Check if any permission asked has been denied for (int i = 0; i < grantResults.length; i++) { if (grantResults[i] != PackageManager.PERMISSION_GRANTED) { anyPermissionDenied = true; //check if user select "never ask again" when denying any permission if (!shouldShowRequestPermissionRationale(permissions[i])) { neverAskAgainSelected = true; } } } if (!anyPermissionDenied) { // All Permissions asked were granted! Yey! // DO YOUR STUFF } else { // the user has just denied one or all of the permissions // use this message to explain why he needs to grant these permissions in order to proceed String message = ""; DialogInterface.OnClickListener listener = null; if (neverAskAgainSelected) { //This message is displayed after the user has checked never ask again checkbox. message = getString(R.string.permission_denied_never_ask_again_dialog_message); listener = new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { //this will be executed if User clicks OK button. This is gonna take the user to the App Settings startAppSettingsConfigActivity(); } }; } else { //This message is displayed while the user hasn't checked never ask again checkbox. message = getString(R.string.permission_denied_dialog_message); } new AlertDialog.Builder(getActivity(), R.style.AlertDialogTheme) .setMessage(message) .setPositiveButton(getString(R.string.label_Ok), listener) .setNegativeButton(getString(R.string.label_cancel), null) .create() .show(); } } break; default: super.onRequestPermissionsResult(requestCode, permissions, grantResults); } } /** * start the App Settings Activity so that the user can change * settings related to the application such as permissions. */ private void startAppSettingsConfigActivity() { final Intent i = new Intent(); i.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); i.addCategory(Intent.CATEGORY_DEFAULT); i.setData(Uri.parse("package:" + getActivity().getPackageName())); i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); i.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); i.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); getActivity().startActivity(i); } 

对于SPECIAL权限的 shouldShowRequestPermissionRationale 总是返回TRUE ONLY在用户拒绝没有checkbox之后

我们对FALSE值感兴趣

所以有3起案件有虚假价值损失:

1.以前没有这样的行动,现在用户决定同意或否认。

只需定义一个首选项ASKED_PERMISSION_* ,它现在不存在,并且在onRequestPermissionsResulttrue ,在任何情况下同意或拒绝

所以,虽然这个偏好不存在, 没有理由检查shouldShowRequestPermissionRationale

2.用户点击同意。

简单地做:

 checkCallingOrSelfPermission(permission) == PackageManager.PERMISSION_GRANTED 

这将返回true并没有理由检查shouldShowRequestPermissionRationale

3.用户点击拒绝与checkbox(第二次或更多的时间问)

这是使用shouldShowRequestPermissionRationale ,它将返回FALSE

(偏好存在,我们没有权限)