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 各种魔改。我个人的处理策略分三步:

  1. 临时拒绝:弹一个友好的说明,告诉用户为什么需要这个权限,然后重新请求。
  2. 永久拒绝:弹一个对话框,提供"去设置"按钮,点击后跳转到应用详情页。
  3. 完全拒绝:如果用户连设置都不给开,那就降级功能,别直接闪退。

跳转设置页的代码:

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 流程图)

我把整个权限检查的逻辑画成了流程图,你一看就明白:

开始权限检查 checkSelfPermission 已授权? 执行功能 永久拒绝? 引导去设置 弹说明 + 重新请求

4.5 实战中的几个细节

最后分享几个我实际项目里积累的点:

  • 不要在主线程做权限检查:虽然 checkSelfPermission 本身很快,但后续的弹窗、跳转设置都是 UI 操作,确保你在主线程处理。
  • 权限被撤销的情况:Android 11 之后,用户如果长时间不用某个权限,系统会自动撤销。所以每次使用前都检查一下,别缓存结果。
  • 多权限请求:如果同时需要多个权限,建议一次性请求,别一个一个弹。用户会烦的。
一个小技巧:我习惯在 onResume 里做一次权限检查。因为用户可能从设置页返回,这时候重新检查能及时更新 UI 状态。

好了,权限检查这块就这些。说白了就是三步:查状态、判类型、做处理。把这三个环节理清楚,你的权限模块就稳了。

公众号:蓝海资料掘金营,微信deep3321