0

0

Android 12 (API 31) 通知操作失效问题及解决方案

DDD

DDD

发布时间:2025-11-25 14:10:25

|

847人浏览过

|

来源于php中文网

原创

Android 12 (API 31) 通知操作失效问题及解决方案

本文深入探讨了在android 12 (api 31) 及更高版本中,通知操作按钮失效的问题及其解决方案。核心在于理解 `pendingintent` 标志的更新要求,即必须明确指定 `flag_immutable` 或 `flag_mutable`。文章提供了具体的代码示例,展示如何根据android版本动态调整 `pendingintent` 的创建方式,并讨论了相关的权限配置和最佳实践,以确保应用在最新android版本上的通知功能正常且安全。

在开发Android应用时,通知(Notification)是与用户交互的重要方式之一,尤其是在实时通信或需要用户立即响应的场景中。然而,随着Android系统版本的迭代,特别是从Android 12 (API 31) 开始,系统对 PendingIntent 的行为引入了严格的变更,这可能导致原有的通知操作按钮在这些新版本上失效。本文将详细解析这一问题,并提供一套完整的解决方案。

Android 12 (API 31) 中 PendingIntent 的行为变更

从Android 12 (API 31) 开始,Android系统强制要求在创建 PendingIntent 时必须明确指定其可变性。这意味着,开发者必须使用 FLAG_IMMUTABLE 或 FLAG_MUTABLE 中的一个标志。

  • FLAG_IMMUTABLE (推荐): 表示创建的 PendingIntent 是不可变的。一旦创建,其内部的 Intent 无法被修改。这提供了更好的安全性,因为系统可以保证在 PendingIntent 发送后,其目标 Intent 不会被恶意修改。对于大多数通知操作,如启动一个特定的Activity或发送一个固定的广播,FLAG_IMMUTABLE 是首选。
  • FLAG_MUTABLE: 表示创建的 PendingIntent 是可变的。这意味着其内部的 Intent 可以在稍后被修改(例如,通过 fillIn() 方法)。如果你的应用确实需要在 PendingIntent 发送后修改其 Intent 的额外数据,则必须使用此标志。然而,使用 FLAG_MUTABLE 需要谨慎,因为它可能引入安全风险,如果 PendingIntent 落入恶意应用之手,它们可能会修改其内容并执行未经授权的操作。

如果未指定这两个标志中的任何一个,系统将抛出 IllegalArgumentException,或者在某些情况下, PendingIntent 会直接失效,导致通知操作按钮无响应。

解决方案:动态调整 PendingIntent 标志

为了兼容不同版本的Android系统,我们需要在创建 PendingIntent 时,根据当前的SDK版本动态地添加 FLAG_IMMUTABLE 标志。

以下是针对通知操作中 PendingIntent 的修改示例:

import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import androidx.annotation.NonNull;
import androidx.core.app.NotificationCompat;

// ... 在你的FirebaseService.java 或其他处理通知的类中 ...

// 假设 receiveCallAction 和 cancelCallAction 是已经准备好的 Intent 对象

PendingIntent receiveCallPendingIntent;
PendingIntent cancelCallPendingIntent;

// 针对 Android 12 (API 31) 及更高版本,必须添加 FLAG_IMMUTABLE 或 FLAG_MUTABLE
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
    // 对于大多数通知操作,FLAG_IMMUTABLE 是更安全且推荐的选择
    receiveCallPendingIntent = PendingIntent.getBroadcast(this, 1200, receiveCallAction, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT);
    cancelCallPendingIntent = PendingIntent.getBroadcast(this, 1201, cancelCallAction, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT);
} else {
    // 对于 Android 12 以下的版本,FLAG_UPDATE_CURRENT 仍然适用
    receiveCallPendingIntent = PendingIntent.getBroadcast(this, 1200, receiveCallAction, PendingIntent.FLAG_UPDATE_CURRENT);
    cancelCallPendingIntent = PendingIntent.getBroadcast(this, 1201, cancelCallAction, PendingIntent.FLAG_UPDATE_CURRENT);
}

// 将修改后的 PendingIntent 添加到通知构建器中
notificationBuilder
        .addAction(R.drawable.ic_baseline_call_24_green, "接听", receiveCallPendingIntent)
        .addAction(R.drawable.ic_baseline_call_red_24, "拒绝", cancelCallPendingIntent);

// ... 其他通知构建逻辑 ...

代码解析:

Dora
Dora

创建令人惊叹的3D动画网站,无需编写一行代码。

下载
  1. 版本检查: if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) 用于判断当前设备是否运行在 Android 12 (API 31) 或更高版本。
  2. 添加 FLAG_IMMUTABLE: 如果是 Android 12 及更高版本,我们通过位或操作符 | 将 PendingIntent.FLAG_IMMUTABLE 与 PendingIntent.FLAG_UPDATE_CURRENT 结合。FLAG_UPDATE_CURRENT 仍然有用,它指示如果 PendingIntent 已经存在,则更新其额外数据。
  3. 兼容旧版本: 对于低于 Android 12 的版本,我们保持原有的 PendingIntent.FLAG_UPDATE_CURRENT 即可,无需添加新的可变性标志。

清单文件 (AndroidManifest.xml) 配置考虑

除了 PendingIntent 标志的修改,针对像通话应用这类需要特殊权限和后台运行能力的场景,还需要在 AndroidManifest.xml 中进行相应的配置。

1. 权限声明

确保你的应用声明了必要的权限。虽然 PendingIntent 标志的修改是核心,但以下权限在特定场景下可能也很重要:

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<!-- 其他必要的权限,如 INTERNET, FOREGROUND_SERVICE 等 -->
  • android.permission.SYSTEM_ALERT_WINDOW: 如果你的应用需要在其他应用之上显示悬浮窗(例如,来电界面在锁屏或后台显示),则需要此权限。
  • android.permission.RECEIVE_BOOT_COMPLETED: 如果你的应用需要在设备启动完成后执行某些操作(例如,重新启动服务或检查未接来电),则需要此权限。

2. 广播接收器 (BroadcastReceiver) 配置

对于处理来电等事件的 CallReceiver,其配置也需要注意。

<receiver
    android:name=".receiver.CallReceiver"
    android:exported="false"
    android:enabled="true"
    android:permission="android.permission.RECEIVE_BOOT_COMPLETED" >
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
    <!-- 如果你的通知操作需要通过隐式 Intent 触发此 Receiver,
         且 Receiver 内部处理了特定的 Action,则需要在此处声明对应的 Action
         例如:
    <intent-filter>
        <action android:name="RECEIVE_CALL" />
        <action android:name="CANCEL_CALL" />
    </intent-filter>
    -->
</receiver>

配置解析:

  • android:exported="false": 强烈建议将你的 BroadcastReceiver 设置为 exported="false",除非你确实需要它接收来自其他应用的广播。这可以增强应用的安全性,防止恶意应用向你的接收器发送广播。
  • android:enabled="true": 确保接收器是启用的。
  • android:permission="android.permission.RECEIVE_BOOT_COMPLETED": 如果 BroadcastReceiver 监听 BOOT_COMPLETED 事件,并且你希望限制只有拥有此权限的应用才能向其发送此广播(这通常不是针对 BOOT_COMPLETED 事件本身,而是对接收器整体的访问权限控制),可以添加此属性。然而,对于 BOOT_COMPLETED 这种系统广播,通常不需要在接收器上额外声明权限。此属性更常用于自定义权限,以保护自定义广播。
  • intent-filter: 声明 BroadcastReceiver 能够响应的 Intent 类型。
    • android.intent.action.BOOT_COMPLETED: 允许接收器在设备启动完成后被触发。
    • 对于通过通知操作触发的广播,通常是通过显式 Intent 结合 PendingIntent.getBroadcast() 发送的,因此 BroadcastReceiver 不一定需要一个匹配的 intent-filter 来响应这些显式 Intent。但是,如果 PendingIntent 使用了隐式 Intent 或 Intent 的 action 属性,那么 intent-filter 就变得至关重要。在示例中,通知操作是发送给 CallReceiver 的显式 Intent,并设置了 action 字符串("RECEIVE_CALL", "CANCEL_CALL"),因此 CallReceiver 内部通过 intent.getStringExtra("action") 来区分操作。

注意事项与最佳实践

  1. 安全性优先: 尽可能使用 FLAG_IMMUTABLE。只有在确实需要修改 PendingIntent 内部 Intent 的情况下才考虑使用 FLAG_MUTABLE,并且要充分理解其潜在的安全风险。
  2. 前台服务 (Foreground Service): 对于通话或音乐播放等需要长时间运行且用户可见的任务,强烈建议使用前台服务。前台服务会显示一个持久的通知,告知用户应用正在后台运行,并可以提高应用的优先级,减少被系统终止的可能性。
  3. 测试兼容性: 在不同版本的Android设备上进行充分测试,尤其是 Android 12 (API 31) 及更高版本,以确保所有通知功能都能正常工作。
  4. 明确 Intent 行为: 确保你的 Intent 目标明确,无论是启动 Activity、Service 还是发送 Broadcast。对于 BroadcastReceiver,如果 PendingIntent 使用显式 Intent (即 new Intent(context, YourReceiver.class)), 则 BroadcastReceiver 不一定需要一个匹配的 intent-filter 来响应。但如果 Intent 是隐式的,或者你希望通过 action 属性来路由,则 intent-filter 必不可少。

通过以上修改和注意事项,你的应用将能够更好地适应 Android 12 (API 31) 及更高版本的系统行为,确保通知操作功能在所有兼容设备上都能正常、安全地运行。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
if什么意思
if什么意思

if的意思是“如果”的条件。它是一个用于引导条件语句的关键词,用于根据特定条件的真假情况来执行不同的代码块。本专题提供if什么意思的相关文章,供大家免费阅读。

846

2023.08.22

pdf怎么转换成xml格式
pdf怎么转换成xml格式

将 pdf 转换为 xml 的方法:1. 使用在线转换器;2. 使用桌面软件(如 adobe acrobat、itext);3. 使用命令行工具(如 pdftoxml)。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

1946

2024.04.01

xml怎么变成word
xml怎么变成word

步骤:1. 导入 xml 文件;2. 选择 xml 结构;3. 映射 xml 元素到 word 元素;4. 生成 word 文档。提示:确保 xml 文件结构良好,并预览 word 文档以验证转换是否成功。想了解更多xml的相关内容,可以阅读本专题下面的文章。

2119

2024.08.01

xml是什么格式的文件
xml是什么格式的文件

xml是一种纯文本格式的文件。xml指的是可扩展标记语言,标准通用标记语言的子集,是一种用于标记电子文件使其具有结构性的标记语言。想了解更多相关的内容,可阅读本专题下面的相关文章。

1168

2024.11.28

js 字符串转数组
js 字符串转数组

js字符串转数组的方法:1、使用“split()”方法;2、使用“Array.from()”方法;3、使用for循环遍历;4、使用“Array.split()”方法。本专题为大家提供js字符串转数组的相关的文章、下载、课程内容,供大家免费下载体验。

760

2023.08.03

js截取字符串的方法
js截取字符串的方法

js截取字符串的方法有substring()方法、substr()方法、slice()方法、split()方法和slice()方法。本专题为大家提供字符串相关的文章、下载、课程内容,供大家免费下载体验。

221

2023.09.04

java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

1566

2023.10.24

字符串介绍
字符串介绍

字符串是一种数据类型,它可以是任何文本,包括字母、数字、符号等。字符串可以由不同的字符组成,例如空格、标点符号、数字等。在编程中,字符串通常用引号括起来,如单引号、双引号或反引号。想了解更多字符串的相关内容,可以阅读本专题下面的文章。

649

2023.11.24

C# ASP.NET Core微服务架构与API网关实践
C# ASP.NET Core微服务架构与API网关实践

本专题围绕 C# 在现代后端架构中的微服务实践展开,系统讲解基于 ASP.NET Core 构建可扩展服务体系的核心方法。内容涵盖服务拆分策略、RESTful API 设计、服务间通信、API 网关统一入口管理以及服务治理机制。通过真实项目案例,帮助开发者掌握构建高可用微服务系统的关键技术,提高系统的可扩展性与维护效率。

3

2026.03.11

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Kotlin 教程
Kotlin 教程

共23课时 | 4.3万人学习

C# 教程
C# 教程

共94课时 | 11.1万人学习

Java 教程
Java 教程

共578课时 | 80.6万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号