3、运行时请求:ActivityCompat.requestPermissions、权限回调处理、用户授权流程
好,咱们接着聊运行时权限。上一章我们把权限声明和检查讲透了,这一章进入真正的“临门一脚”——怎么向用户发起请求,怎么处理用户的回应。
说实话,这个环节是很多开发者翻车的地方。我自己刚接触动态权限时,也踩过不少坑。比如有一次,我天真地以为用户点了“允许”就万事大吉,结果在回调里忘了判断权限是否真的授予了——嗯,后来被测试小姐姐追着改bug,记忆犹新。
3.1 发起请求:ActivityCompat.requestPermissions
检查完权限,发现没授权,那就得请求了。Android提供了标准API:
// 检查权限
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED) {
// 没授权,发起请求
ActivityCompat.requestPermissions(
this,
new String[]{Manifest.permission.CAMERA},
REQUEST_CODE_CAMERA
);
} else {
// 已有权限,直接干活
openCamera();
}
这里有几个关键点,我挨个说一下:
- 第一个参数是Activity或Fragment:如果你在Fragment里调用,建议用Fragment自己的requestPermissions方法,而不是ActivityCompat。否则回调会走到Activity里,处理起来麻烦。
- 第二个参数是权限数组:可以一次请求多个权限。但我个人习惯一次只请求一个,用户体验更好。你想想看,用户突然看到一堆弹窗,大概率会烦躁。
- 第三个参数是请求码:随便定义一个int常量,只要在回调里能区分是哪个请求就行。我一般用0x10、0x20这样的十六进制,看着清晰。
💡 我的小技巧: 请求码不要用0、1、2这种太简单的数字,万一和其他请求码冲突了,调试起来很头疼。我习惯用权限名的hashCode取低16位,基本不会重复。
3.2 权限回调处理:onRequestPermissionsResult
用户看到弹窗后,会点“允许”或“拒绝”。结果会通过onRequestPermissionsResult回调告诉你。这是整个流程的核心,写不好就出大问题。
@Override
public void onRequestPermissionsResult(int requestCode,
@NonNull String[] permissions,
@NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == REQUEST_CODE_CAMERA) {
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// 用户同意了,开干
openCamera();
} else {
// 用户拒绝了
showPermissionDeniedDialog();
}
}
}
这段代码看起来简单,但有几个坑:
- grantResults可能为空:如果用户取消了弹窗(比如按了返回键),grantResults数组长度就是0。所以一定要先判空再取值。
- 权限和结果一一对应:permissions数组和grantResults数组的顺序是一致的。permissions[0]对应grantResults[0],以此类推。
- 别忘了调用super:虽然不调也不一定会崩,但某些系统行为可能受影响。我建议养成好习惯,先调super。
⚠️ 我曾经踩过的坑: 有一次我在Fragment里请求权限,但回调写在了Activity里。结果用户点了允许,Fragment那边完全没反应。后来才发现,Fragment的requestPermissions会回调到Fragment自己的onRequestPermissionsResult,而不是Activity的。所以,谁发起请求,谁处理回调。
3.3 用户授权流程:从弹窗到决策
整个授权流程,说白了就是用户和系统的一次对话。我画了一张图,帮你理清逻辑:
用户看到弹窗后,通常有三种反应:
| 用户操作 | grantResults值 | 后续处理 |
|---|---|---|
| 点击“允许” | PERMISSION_GRANTED | 直接执行需要权限的功能 |
| 点击“拒绝” | PERMISSION_DENIED | 提示用户功能受限,不要反复弹窗 |
| 点击“拒绝并不再询问” | PERMISSION_DENIED | 引导用户去设置页面手动开启 |
重点来了: 用户拒绝后,下次再请求时,系统可能不再弹窗。这时候你需要用 shouldShowRequestPermissionRationale 来判断。如果返回false,说明用户选了“不再询问”,你就得引导他去设置里手动开启。
// 判断是否需要显示解释说明
if (shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA)) {
// 用户拒绝过但没选“不再询问”,可以再解释一下
showRationaleDialog();
} else {
// 用户选了“不再询问”,直接引导去设置
goToAppSettings();
}
我个人习惯在用户第一次拒绝时,弹一个友好的提示框,解释为什么需要这个权限。如果用户再次拒绝,就不再打扰了,直接引导去设置。这样既尊重用户,又不至于让用户觉得烦。
💡 避坑指南: 我曾经在一个项目里,用户拒绝后直接再次请求权限,结果被系统判定为“骚扰”,弹窗都不显示了。后来我加了一个判断:如果用户拒绝超过一次,就不再自动请求,而是显示一个“去设置开启”的按钮。用户体验好了很多。
好了,运行时请求的核心内容就这些。记住三个关键点:发起请求用requestPermissions,处理结果在onRequestPermissionsResult,用户拒绝后要合理引导。下一章我们会聊更高级的话题——权限分组和特殊权限的处理,到时候见。