求助:实现Runtime Permission后位置权限回调反复触发问题
问题根源分析
你遇到的onRequestPermissionResult反复调用的死循环,核心原因是在onResume中无条件触发权限请求:
当用户勾选「不再询问」后,系统会直接拒绝权限请求并回调onRequestPermissionResult,而权限请求操作会触发Activity的生命周期回调(比如onResume再次执行),这样就形成了:onResume → 请求权限 → onRequestPermissionResult → onResume → ... 的无限循环。
另外,当用户勾选「不再询问」后,系统不会再弹出权限请求弹窗,此时继续调用requestPermissions不仅无效,还会加剧循环问题。
修复方案
我会基于你提供的代码片段,给出完整的修复逻辑,核心是区分权限请求的不同状态,避免无意义的循环:
1. 完善权限检查方法
首先确保权限检查的逻辑正确:
private fun checkLocationPermission(): Boolean { return ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED }
2. 修改onResume逻辑,避免循环请求
在onResume中,我们需要先判断权限状态、用户是否勾选「不再询问」,再决定是执行业务逻辑、显示权限解释还是引导到设置页:
private val LOCATION_PERMISSION_REQUEST_CODE = 1001 override fun onResume() { super.onResume() if (checkLocationPermission()) { // 权限已授予,执行你的startL逻辑 startL() } else { handleLocationPermissionRequest() } } private fun handleLocationPermissionRequest() { val shouldShowRationale = ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.ACCESS_FINE_LOCATION) val prefs = getSharedPreferences("PermissionPrefs", MODE_PRIVATE) val hasRequestedPermission = prefs.getBoolean("hasRequestedLocationPermission", false) when { shouldShowRationale -> { // 用户之前拒绝过但没勾选「不再询问」,显示权限用途解释 Snackbar.make(findViewById(android.R.id.content), "需要位置权限来获取您的位置信息,以便正常使用相关功能", Snackbar.LENGTH_SHORT) .setAction("知道了") { requestPermissions(arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), LOCATION_PERMISSION_REQUEST_CODE) } .show() } !hasRequestedPermission -> { // 第一次请求权限,直接发起请求 requestPermissions(arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), LOCATION_PERMISSION_REQUEST_CODE) prefs.edit().putBoolean("hasRequestedLocationPermission", true).apply() } else -> { // 用户已勾选「不再询问」,引导到应用设置页开启权限 Snackbar.make(findViewById(android.R.id.content), "位置权限被拒绝且不再提示,请前往设置开启权限", Snackbar.LENGTH_INDEFINITE) .setAction("去设置") { val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) val uri = Uri.fromParts("package", packageName, null) intent.data = uri startActivity(intent) } .show() } } }
3. 正确处理onRequestPermissionResult回调
在回调中,我们只根据权限结果执行对应操作,不再触发新的权限请求:
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) if (requestCode == LOCATION_PERMISSION_REQUEST_CODE) { if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) { // 权限授予成功,执行业务逻辑 startL() } else { // 权限被拒绝,再次检查是否需要引导到设置页 if (!ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.ACCESS_FINE_LOCATION)) { // 用户勾选了「不再询问」,直接引导到设置 Snackbar.make(findViewById(android.R.id.content), "需要位置权限才能正常使用,请前往设置开启", Snackbar.LENGTH_INDEFINITE) .setAction("去设置") { val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) val uri = Uri.fromParts("package", packageName, null) intent.data = uri startActivity(intent) } .show() } else { // 用户只是拒绝,没勾选不再询问,给出提示即可 Snackbar.make(findViewById(android.R.id.content), "位置权限被拒绝,部分功能无法使用", Snackbar.LENGTH_SHORT).show() } } } }
关键注意点
- 避免在生命周期方法中无条件请求权限:
onResume会在很多场景下触发(比如权限请求回调后、从设置页返回后),必须加状态判断; - 利用
shouldShowRequestPermissionRationale区分用户状态:这个方法返回false时,要么是第一次请求权限,要么是用户勾选了「不再询问」,所以需要用SharedPreferences记录请求历史; - 用户勾选「不再询问」后,直接引导到设置页:此时系统不会再弹出权限弹窗,只能引导用户手动开启。
内容的提问来源于stack exchange,提问作者Ashiqul Islam
相关产品推荐
相关产品推荐

