4. 运行时权限请求流程:检查权限状态、请求权限对话框、处理用户选择结果
Android 13 将通知权限(POST_NOTIFICATIONS)纳入运行时权限体系。应用在首次发送通知前,必须先通过标准的运行时权限流程:检查 → 请求 → 处理用户选择。本章详解每一步的原理、API 调用、代码实现及调试方法。
概念要点: 运行时权限在 Android 6.0 (API 23) 引入,Android 13 新增通知权限属于
dangerous permission。系统弹窗由 PackageManager 控制,用户可选择“允许”、“拒绝”或“不再询问”。
环境前提: 确保
targetSdkVersion >= 33,并在 AndroidManifest.xml 声明 <uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>。
4.1 检查权限状态
在发起请求前必须检查当前权限状态,避免重复弹窗。使用 ContextCompat.checkSelfPermission(),返回 PackageManager.PERMISSION_GRANTED (0) 或 PERMISSION_DENIED (-1)。
if (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS)
== PackageManager.PERMISSION_GRANTED) {
// 权限已授予,直接发送通知
} else {
// 权限未授予,准备请求
}
如果当前 API 版本低于 33(Android 13),则直接视为权限已授予(通知权限在低版本系统上由安装时授权管理)。
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
// 低版本无条件允许发送通知
return;
}
4.2 请求权限对话框
当权限未授予时,调用 ActivityCompat.requestPermissions() 触发系统弹窗。该方法需要传入当前 Activity、权限数组和请求码。
private static final int NOTIFICATION_PERMISSION_REQUEST_CODE = 100;
ActivityCompat.requestPermissions(
this,
new String[]{ Manifest.permission.POST_NOTIFICATIONS },
NOTIFICATION_PERMISSION_REQUEST_CODE
);
重要: 系统弹窗一次只能展示一个权限(单个 Dialog),不可批量请求。用户每次选择后,系统会记住选择(允许/拒绝/不再询问),下次调用
requestPermissions() 可能直接返回结果而不弹窗。
4.3 处理用户选择结果
系统通过 Activity 的 onRequestPermissionsResult() 回调返回结果。需重写该方法,判断请求码、权限名和 grantResults。
@Override
public void onRequestPermissionsResult(int requestCode,
@NonNull String[] permissions,
@NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == NOTIFICATION_PERMISSION_REQUEST_CODE) {
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// 用户同意 → 发送通知
sendNotification();
} else {
// 用户拒绝
if (!ActivityCompat.shouldShowRequestPermissionRationale(
this, Manifest.permission.POST_NOTIFICATIONS)) {
// 用户勾选“不再询问”或永久拒绝
showSettingsRedirectDialog();
} else {
// 普通拒绝,可以再次解释后重新请求
showPermissionExplanationSnackbar();
}
}
}
}
常见陷阱: 在 Fragment 中处理时,使用
requestPermissions() 和 onRequestPermissionsResult() 需通过 Fragment 自身调用,而非 Activity。在 Compose 中建议使用 rememberLauncherForActivityResult 或 PermissionState。
4.4 调试与验证
使用 ADB 命令模拟权限状态,快速验证逻辑:
# 授予通知权限
adb shell pm grant com.example.app android.permission.POST_NOTIFICATIONS
# 撤销通知权限
adb shell pm revoke com.example.app android.permission.POST_NOTIFICATIONS
# 查看当前应用权限列表
adb shell dumpsys package com.example.app | grep -A10 "requested permissions"
在 Logcat 中过滤权限相关日志:
adb logcat -s "PackageManager:V NotificationService:V"
4.5 完整实现流程(Activity 示例)
| 步骤 | 操作 | 关键方法/代码 |
|---|---|---|
| 1 | 权限检查 | checkSelfPermission(POST_NOTIFICATIONS) |
| 2 | 条件判断 | 已授权 → 发送通知;未授权 → 下一步 |
| 3 | 请求权限 | requestPermissions(…) |
| 4 | 处理回调 | onRequestPermissionsResult 判断 grantResults |
| 5 | 结果分支 | 同意 → 执行通知;拒绝 → 引导或解释 |
public class MainActivity extends AppCompatActivity {
private static final int REQ_NOTIFICATION = 1001;
public void onClickSendNotification(View v) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS)
== PackageManager.PERMISSION_GRANTED) {
sendNotification();
} else {
ActivityCompat.requestPermissions(this,
new String[]{ Manifest.permission.POST_NOTIFICATIONS },
REQ_NOTIFICATION);
}
} else {
sendNotification();
}
}
@Override
public void onRequestPermissionsResult(int requestCode,
@NonNull String[] permissions,
@NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == REQ_NOTIFICATION) {
if (grantResults.length > 0 && grantResults[0] == PERMISSION_GRANTED) {
sendNotification();
} else {
// 拒绝处理
if (!ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.POST_NOTIFICATIONS)) {
// 不再询问 → 引导用户去设置
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
Uri.parse("package:" + getPackageName()));
startActivity(intent);
} else {
// 普通拒绝 → 显示解释后再次请求
Toast.makeText(this, "需要通知权限才能发送提醒", Toast.LENGTH_LONG).show();
}
}
}
}
private void sendNotification() {
// 发送通知逻辑(第5章详解)
}
}
4.6 常见错误与优化
- 错误: 在未声明权限的 AndroidManifest.xml 中请求 → 会立即收到
PERMISSION_DENIED,且无弹窗。 - 错误: 在子线程调用
requestPermissions()→ 抛出IllegalStateException。 - 优化: 检测用户曾拒绝并勾选“不再询问”后,直接跳转应用设置页,避免无谓弹窗。
- 优化: 在 Activity 的
onResume()中再次检查权限状态,处理用户从设置页返回后的状态变化。
性能提醒: 权限请求是同步操作(检查阶段),不会阻塞主线程;但弹窗由系统服务异步处理,回调保证在主线程执行。无需额外线程同步。
至此,你已经掌握 Android 13 通知权限的完整运行时请求流程。下一步(第5章)将实现通知渠道创建和通知发送,将本章获取的权限转化为真实通知。