4、权限检查:ContextCompat.checkSelfPermission、权限状态判断、权限拒绝处理
好,咱们接着聊。上一节我们把权限声明搞定了,但声明归声明,真正运行时还得检查——用户可能随时在设置里关掉权限。这节我就把权限检查这块掰开揉碎讲清楚。
4.1 核心方法:ContextCompat.checkSelfPermission
检查权限,最常用的就是 ContextCompat.checkSelfPermission。为什么推荐用这个?因为它帮我们处理了不同 Android 版本的差异。你想想看,要是自己写 Context.checkSelfPermission,在低版本上可能就崩了。
用法很简单:
int permissionState = ContextCompat.checkSelfPermission(
context,
Manifest.permission.CAMERA
);
返回结果只有两种:
PackageManager.PERMISSION_GRANTED—— 已授权PackageManager.PERMISSION_DENIED—— 未授权
嗯,这里要注意:这个方法不会弹窗,不会请求,只是单纯查一下当前状态。我见过不少新手以为调了这个就会弹出授权对话框,结果代码跑半天没反应。
个人习惯:我一般把检查逻辑封装成一个工具方法,比如
hasPermission(context, permission),返回 boolean。这样业务代码里写起来清爽很多。
4.2 权限状态判断:不只是 GRANTED 和 DENIED
你以为权限状态就两种?其实没那么简单。Android 6.0 之后,权限状态其实有三种语义:
| 状态 | 含义 | 用户行为 |
|---|---|---|
| GRANTED | 已授权 | 用户允许了 |
| DENIED(未勾选"不再询问") | 临时拒绝 | 用户点了拒绝,但下次还能弹窗 |
| DENIED(已勾选"不再询问") | 永久拒绝 | 用户点了拒绝并勾选不再询问 |
为什么要区分后两种?因为处理方式完全不同。临时拒绝,你直接再弹一次请求就行。永久拒绝,你再弹窗也没用,得引导用户去设置里手动打开。
判断是否"永久拒绝"的方法:
boolean shouldShowRationale = ActivityCompat.shouldShowRequestPermissionRationale(
activity,
Manifest.permission.CAMERA
);
// 如果返回 false,且权限状态是 DENIED,那就是永久拒绝了
if (!shouldShowRationale &&
ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA)
== PackageManager.PERMISSION_DENIED) {
// 引导用户去设置
}
我曾经踩过的坑:第一次请求权限时,
shouldShowRequestPermissionRationale 返回 false。这不是永久拒绝,而是系统还没记录用户的选择。所以判断永久拒绝时,一定要先确认权限状态是 DENIED。
4.3 权限拒绝处理:优雅地引导用户
权限被拒绝是常态,尤其是国内 ROM 各种魔改。我个人的处理策略分三步:
- 临时拒绝:弹一个友好的说明,告诉用户为什么需要这个权限,然后重新请求。
- 永久拒绝:弹一个对话框,提供"去设置"按钮,点击后跳转到应用详情页。
- 完全拒绝:如果用户连设置都不给开,那就降级功能,别直接闪退。
跳转设置页的代码:
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package", getPackageName(), null);
intent.setData(uri);
startActivity(intent);
这里有个细节:跳转后最好用 startActivityForResult,这样用户从设置页返回时,你可以重新检查权限状态,决定下一步操作。
核心原则:永远不要让用户因为权限问题卡死。权限拿不到,功能降级;功能降不了,给个清晰的提示。最忌讳的是直接崩溃或者弹个空白页面。
4.4 完整检查流程(SVG 流程图)
我把整个权限检查的逻辑画成了流程图,你一看就明白:
4.5 实战中的几个细节
最后分享几个我实际项目里积累的点:
- 不要在主线程做权限检查:虽然
checkSelfPermission本身很快,但后续的弹窗、跳转设置都是 UI 操作,确保你在主线程处理。 - 权限被撤销的情况:Android 11 之后,用户如果长时间不用某个权限,系统会自动撤销。所以每次使用前都检查一下,别缓存结果。
- 多权限请求:如果同时需要多个权限,建议一次性请求,别一个一个弹。用户会烦的。
一个小技巧:我习惯在
onResume 里做一次权限检查。因为用户可能从设置页返回,这时候重新检查能及时更新 UI 状态。
好了,权限检查这块就这些。说白了就是三步:查状态、判类型、做处理。把这三个环节理清楚,你的权限模块就稳了。