Android运行时权限疑问:已授权权限为何被重复请求
我猜你大概率是在权限检查或请求的逻辑上没理清开关对应的目标权限,或者你的hasPermissions方法实现有漏洞,导致已经授权的联系人权限被错误地加入了请求列表。咱们一步步拆解问题:
1. 权限请求逻辑未区分开关对应的目标权限
你提到点击联系人开关时会请求两类权限,但点击位置开关时,理论上只需要处理位置权限就够了。如果你的代码不管点击哪个开关,都直接请求「联系人+位置」这一组权限——哪怕其中一个已经授权,再加上你的hasPermissions方法是检查所有权限是否都已授权,那么只要有一个权限未授权(比如位置被手动移除后),就会触发全部权限的请求。
举个典型的错误逻辑例子:
// 错误:点击位置开关时仍请求全部权限 if (!hasPermissions(context, new String[]{Manifest.permission.READ_CONTACTS, Manifest.permission.ACCESS_FINE_LOCATION})) { requestPermissions(this, new String[]{Manifest.permission.READ_CONTACTS, Manifest.permission.ACCESS_FINE_LOCATION}, REQ_CODE); }
正确的做法是,点击位置开关时只检查并请求位置权限:
// 正确:点击位置开关仅处理位置权限 if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, REQ_CODE_LOCATION); }
2. hasPermissions方法的实现存在漏洞
如果你的hasPermissions是批量检查多个权限,并且只有当所有权限都已授权时才返回true,那么当其中一个权限被移除后,这个方法会返回false,导致你重新请求全部权限——哪怕其他权限已经授权。
比如这种错误的实现:
// 错误:只要有一个权限未授权就返回false,触发全量请求 public boolean hasPermissions(Context context, String[] permissions) { for (String perm : permissions) { if (ContextCompat.checkSelfPermission(context, perm) != PackageManager.PERMISSION_GRANTED) { return false; } } return true; }
哪怕联系人权限已经授权,只要位置权限没了,这个方法就返回false,你就会把两个权限都放进请求里。
解决办法是,要么针对单个权限做检查,要么在请求前过滤掉已授权的权限:
// 工具方法:过滤已授权权限,只返回未授权的 public String[] getUnGrantedPermissions(Context context, String[] permissions) { List<String> unGranted = new ArrayList<>(); for (String perm : permissions) { if (ContextCompat.checkSelfPermission(context, perm) != PackageManager.PERMISSION_GRANTED) { unGranted.add(perm); } } return unGranted.toArray(new String[0]); } // 使用方式 String[] unGranted = getUnGrantedPermissions(this, new String[]{Manifest.permission.READ_CONTACTS, Manifest.permission.ACCESS_FINE_LOCATION}); if (unGranted.length > 0) { ActivityCompat.requestPermissions(this, unGranted, REQ_CODE); }
这样哪怕你传入全部权限,也只会请求未授权的那个,不会重复触发已授权权限的请求。
3. 额外提醒:权限组的特性(Android 6.0+)
有些权限属于同一个权限组(比如READ_CONTACTS和WRITE_CONTACTS同属CONTACTS组),如果用户已经授权组内一个权限,请求同组其他权限时系统会自动授权,不会弹对话框。但联系人与位置是完全不同的权限组,所以这个点不是你问题的原因,但可以留意。
最后给你个排查步骤:
- 确认点击位置开关时,代码是否只针对位置权限做检查和请求,而非全量权限。
- 检查
hasPermissions方法的实现,是否正确判断单个权限的授权状态。 - 养成请求前过滤已授权权限的习惯,避免不必要的请求。
内容的提问来源于stack exchange,提问作者Jayavinoth

