Android 11+如何区分权限「永久拒绝」与「每次询问」状态
区分Android 11+中位置权限「永久拒绝」与「每次询问」状态的方案
我近期开发检查并请求位置权限的方法,遵循Android官方权限请求指南实现逻辑,还添加了处理用户永久拒绝权限的逻辑。原本通过shouldShowRequestPermissionRationale()方法结合SharedPreferences存储的首次请求标记判断权限状态,在API<30时能可靠识别永久拒绝,但在Android 11及以上版本出现问题:当用户将权限状态改为「每次询问」后,shouldShowRequestPermissionRationale()返回false,且SharedPreferences标记显示非首次请求,导致误触发「永久拒绝」逻辑,引导用户前往设置而非直接在应用内请求权限。
解决方案
Android 11(API30)新增「每次询问」权限模式,单纯依赖旧逻辑无法区分该状态与永久拒绝,需要结合权限标记查询优化逻辑:
1. 使用API29+的getPermissionFlags()判断永久拒绝
通过PackageManager.getPermissionFlags()获取权限的系统标记,其中FLAG_PERMISSION_POLICY_FIXED表示权限被系统固定为拒绝(即用户选择了「不再询问」),「每次询问」模式下该标记不会被设置。
调整后的权限处理逻辑:
// 定义请求码常量 private const val REQUEST_CODE_LOCATION = 1001 when { ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED -> { // 权限已授予,执行获取位置信息逻辑 } shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION) -> { // 用户之前拒绝权限但未选择「不再询问」,显示权限说明弹窗 } SharedPreferencesHelper.getInstance(requireContext()).locationPermissionAskedBefore() -> { val permissionFlags = requireContext().packageManager.getPermissionFlags( Manifest.permission.ACCESS_FINE_LOCATION, requireContext().packageName ) val isPermanentlyDenied = permissionFlags and PackageManager.FLAG_PERMISSION_POLICY_FIXED != 0 if (isPermanentlyDenied) { // 权限被永久拒绝,引导用户前往应用设置页面修改权限 } else { // 当前为「每次询问」模式,直接发起权限请求 requestPermissions(arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), REQUEST_CODE_LOCATION) } } else -> { // 首次请求权限,标记已询问并发起请求 SharedPreferencesHelper.getInstance(requireContext()).setLocationPermissionAsked(true) requestPermissions(arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), REQUEST_CODE_LOCATION) } }
2. 兼容低版本的工具方法封装
如果需要兼容API<29的设备,可以封装工具方法区分不同版本的判断逻辑:
fun isLocationPermissionPermanentlyDenied(context: Context): Boolean { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { val permissionFlags = context.packageManager.getPermissionFlags( Manifest.permission.ACCESS_FINE_LOCATION, context.packageName ) permissionFlags and PackageManager.FLAG_PERMISSION_POLICY_FIXED != 0 } else { // API<30时:已请求过权限且shouldShow返回false即为永久拒绝 !ActivityCompat.shouldShowRequestPermissionRationale(context as Activity, Manifest.permission.ACCESS_FINE_LOCATION) && SharedPreferencesHelper.getInstance(context).locationPermissionAskedBefore() } }
在主逻辑中直接调用该方法即可完成状态判断。
注意事项
- 「每次询问」模式下,每次发起权限请求都会弹出系统默认的权限弹窗,无需额外显示说明弹窗,直接请求即可。
- 首次请求权限时,务必更新SharedPreferences中的标记,确保后续逻辑判断准确。
内容的提问来源于stack exchange,提问作者amm965
相关产品推荐
相关产品推荐

