18、通知的权限管理:POST_NOTIFICATIONS权限(Android 13+)、运行时权限申请、权限被拒绝的处理策略

通知权限这件事,在 Android 13 之前几乎没人操心。那时候只要在清单文件里声明一下,通知就能随便发。但 Google 从 Android 13(API 33)开始搞了个大动作——把通知权限变成了运行时权限。说白了,现在发通知跟访问相机、读取联系人一样,得弹窗问用户「你同意吗?」。

我个人习惯把这件事分成三个层面来理解:权限声明、运行时申请、被拒绝后的兜底策略。咱们一个一个聊。

18.1 权限声明:清单文件里必须做的事

不管你的 targetSdk 是多少,只要应用跑在 Android 13+ 的设备上,就必须在 AndroidManifest.xml 里加上这个权限声明:

<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />

嗯,这里要注意:如果你的 targetSdk 低于 33,系统会默认帮你兼容,但 Google Play 从 2023 年开始强制要求 targetSdk 33+。所以别想着偷懒,老老实实加上。

⚠️ 警告: 如果你的应用 targetSdk 是 33+ 但没声明这个权限,系统会直接静默拒绝所有通知。连崩溃日志都不会有,用户只会觉得「这 App 怎么不推送了」。

18.2 运行时权限申请:弹窗的正确姿势

权限声明只是第一步。真正让开发者头疼的是运行时申请。我见过不少项目直接在 onCreate 里弹权限对话框,结果用户一脸懵——「这 App 刚打开就要发通知?」。

正确的做法是:在需要发通知的时候才申请。比如用户点击了「开启推送」按钮,或者你确实要发一条重要通知时。

代码其实很简单:

// 检查权限是否已授予
if (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS)
        != PackageManager.PERMISSION_GRANTED) {
    // 没有权限,发起申请
    ActivityCompat.requestPermissions(this,
            new String[]{Manifest.permission.POST_NOTIFICATIONS},
            REQUEST_CODE_POST_NOTIFICATIONS);
} else {
    // 已有权限,直接发通知
    sendNotification();
}

然后在 onRequestPermissionsResult 里处理结果:

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
                                       @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if (requestCode == REQUEST_CODE_POST_NOTIFICATIONS) {
        if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            // 用户同意了
            sendNotification();
        } else {
            // 用户拒绝了
            handlePermissionDenied();
        }
    }
}
💡 个人经验: 我在项目中遇到过一个问题——用户第一次拒绝后,第二次再申请时系统不再弹窗。这是因为 Android 的权限机制有个「不再询问」选项。所以每次申请前最好用 shouldShowRequestPermissionRationale() 判断一下,如果返回 false,说明用户已经勾选了「不再询问」,这时候弹窗也没用,得走引导用户去设置页的流程。

18.3 权限被拒绝的处理策略:别让用户卡死

用户拒绝权限是常态,不是异常。我见过有些 App 在权限被拒后直接弹 Toast「请开启通知权限」,然后什么都不做。用户想开都不知道去哪开。

合理的策略分三种情况:

场景 用户行为 推荐处理方式
第一次拒绝 点击「拒绝」 显示一个友好的说明弹窗,解释为什么需要通知权限,然后再次申请
拒绝并勾选「不再询问」 系统不再弹窗 引导用户去系统设置页手动开启
永久拒绝(设置页关闭) 用户主动关闭 降级处理,比如用轮询代替推送,或者干脆不推送

引导用户去设置页的代码:

private void openAppSettings() {
    Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
    intent.setData(Uri.parse("package:" + getPackageName()));
    startActivity(intent);
}

核心原则: 永远不要强迫用户。通知权限不是核心功能依赖,用户拒绝后你的 App 应该能正常运行,只是少了一些推送体验。

18.4 知识体系总览

下面这张图把整个通知权限管理的流程串起来了。你想想看,从清单声明到运行时申请,再到被拒绝后的三种处理路径,其实就是一个完整的闭环。

通知权限管理流程 1. 清单声明 POST_NOTIFICATIONS 2. 检查权限状态 ✅ 已授权 → 发通知 ❌ 未授权 → 申请 3. 弹窗申请 requestPermissions() 4. 用户选择 同意 → 发通知 5. 拒绝处理策略 引导设置 / 降级 / 说明 ⚠️ 不再询问 shouldShowRationale() Android 13+ 运行时权限 · 三种拒绝场景对应三种处理方式

18.5 避坑指南:我曾经踩过的坑

我曾经在一个项目中犯过一个低级错误——在 Application.onCreate() 里申请通知权限。结果呢?权限对话框还没弹出来,应用就已经在后台发了一条通知。用户看到通知后一脸疑惑:「我还没同意呢,你怎么就发了?」。

后来我才意识到,权限申请必须在 Activity 的上下文中进行。Application 上下文没有窗口,弹不出对话框。而且就算能弹,也不应该在用户还没看到 UI 的时候就申请。

另一个坑是:不要假设用户一定会同意。我见过有些团队在权限被拒后直接让 App 闪退,美其名曰「没有通知权限没法用」。这其实是在绑架用户。通知权限不是核心功能,你的 App 应该能在没有通知的情况下正常运行。

我的建议: 在权限被拒后,可以显示一个非侵入性的提示条(比如 Snackbar),告诉用户「开启通知可以及时收到消息」,然后提供一个「去设置」的按钮。这样既尊重了用户的选择,又给了他们一个便捷的入口。

18.6 总结

通知权限管理说白了就三件事:声明、申请、兜底。声明是基础,申请要讲究时机,兜底要体现对用户的尊重。我个人觉得,Android 13 的这个改动其实是好事——它让用户重新掌握了对自己通知栏的控制权。作为开发者,我们只需要把选择权交给用户,然后优雅地处理每一种结果。

嗯,关于权限管理就聊到这里。记住一句话:权限是用户的,不是你的


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