文章目录
【Android】Notification的基本使用
权限
Android 13(API 33)新增了POST_NOTIFICATIONS
权限,这是一个危险权限,危险权限在 Android 6.0+ 必须运行时动态申请。
- 首先在
AndroidManifest.xml
中声明权限。
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
- 在尝试发送通知前,必须先检查并请求权限。
// Android 13(API 33+)需要动态申请通知权限
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.POST_NOTIFICATIONS}, REQUEST_CODE);
} else {
showNotification();
}
} else {
// Android 13 以下不需要权限,直接发通知
showNotification();
}
ContextCompat.checkSelfPermission(...)
——检查当前是否已经有权限- 返回值是
PackageManager.PERMISSION_GRANTED
,说明用户之前已经允许(或系统已授予) - 返回值是
PackageManager.PERMISSION_DENIED
,说明还没授权,需要发起申请
- 返回值是
ActivityCompat.requestPermissions(...)
—— 发起权限请求(触发系统对话框)- 作用:向系统请求权限。系统会(或不会)弹出系统权限对话框,用户选择「允许/拒绝」
- 参数:
this
:Activity(请求结果会回调到该 Activity 的onRequestPermissionsResult
)String[]
:要请求的权限数组(这里只请求一个)REQUEST_CODE
:你自己定义的请求码,用于回调时区分是哪一次请求(常量,例:private static final int REQUEST_CODE = 1001;
)
- 注意:如果用户之前勾选了“不再询问(Don’t ask again)”,调用
requestPermissions
可能不会弹对话框,而会直接在回调里返回DENIED
。
- 重写
onRequestPermissionsResult(...)
方法——处理用户的选择(回调)
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == REQUEST_CODE) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// 用户同意了通知权限,可以发通知
showNotification();
} else {
// 用户拒绝了通知权限,无法发通知
Toast.makeText(this, "没有通知权限", Toast.LENGTH_SHORT).show();
}
}
}
在Android 13(API 33)之前,通知不需要运行时申请权限(只要应用正常安装,系统默认允许通知)。
通知的基本使用
1. 获取通知管理器(用于发送、更新、取消通知)
NotificationManager
(系统服务类)// 从当前上下文(Context)获取系统服务 NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); // API 23+ 推荐(避免强转,更安全) NotificationManager notificationManager = getSystemService(NotificationManager.class); // 在 Fragment 里(没有直接的 getSystemService) NotificationManager notificationManager = requireContext().getSystemService(NotificationManager.class);
缺点:不同 Android 版本的 API 差异较大。比如 Android 8.0 开始必须使用 通知渠道(NotificationChannel),而老版本没有,需要自己写兼容逻辑。
NotificationManagerCompat
(兼容类,来自 AndroidX 库)NotificationManagerCompat manager = NotificationManagerCompat.from(context);
是对
NotificationManager
的一个 封装,主要作用是 屏蔽 Android 不同版本的差异。
2. 创建通知渠道(Android 8.0+ 必须)
// Android 8.0(API 26+)开始需要创建通知渠道
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
CharSequence name = "Channel Name";
String description = "Channel Description";
int importance = NotificationManager.IMPORTANCE_HIGH;
NotificationChannel channel = new NotificationChannel("my_channel_id", name, importance);
channel.setDescription(description);
notificationManager.createNotificationChannel(channel);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { … }
Build.VERSION.SDK_INT
是当前设备的 API 等级(整数)。Build.VERSION_CODES.O
是 Android 8.0(API 26)。NotificationChannel
及通知渠道机制从 API 26 开始引入,所以这段代码只在 API ≥ 26 时执行,避免在旧设备上因类/方法不存在而崩溃。
CharSequence name = "Channel Name";
- 这是渠道的可见名称(会显示在系统的“应用通知设置 → 渠道”界面)。
- 使用
CharSequence
而不是String
是因为系统允许富文本/本地化。建议用字符串资源(getString(R.string.xxx)
)以便本地化。
String description = "Channel Description";
- 渠道的描述,用户在系统设置里可以看到,说明这个渠道里的通知是什么用途,同样建议放入资源以支持多语言。
int importance = NotificationManager.IMPORTANCE_DEFAULT;
渠道的重要性(importance)决定通知的行为:是否有声音、是否在锁屏显示、是否会弹出(heads-up)等。常见取值(从低到高):
IMPORTANCE_NONE
:不展示(等于关闭这个渠道)。IMPORTANCE_MIN
:最不显眼,只在通知面板很靠后的位置显示,不震动、不响声、不显示图标。IMPORTANCE_LOW
:不响、不震动,但会在状态栏有较低优先级展示。IMPORTANCE_DEFAULT
:有声音/震动(默认行为)。IMPORTANCE_HIGH
(或更高):会产生 Heads-up(弹出)提示,通常用于紧急或需即时注意的通知。
NotificationChannel channel = new NotificationChannel("my_channel_id", name, importance);
创建
NotificationChannel
对象。参数:"my_channel_id"
:渠道 ID,是你在代码里标识该渠道的唯一字符串(在同一应用内应唯一)。- 这个 ID 后续发通知时要在
NotificationCompat.Builder
中使用:new NotificationCompat.Builder(context, "my_channel_id")
。 - 一旦创建并发布给系统,渠道 ID 不可变(你不能重命名 ID)。若需不同设置应新建不同 ID。
- 这个 ID 后续发通知时要在
name
:用户可见的名称。importance
:重要性等级(如上)。
channel.setDescription(description);
- 给渠道设置描述(在系统设置里显示),帮助用户理解该渠道的用途。
notificationManager.createNotificationChannel(channel);
将渠道注册到系统(通过
NotificationManager
)。注册后:- 系统会把该渠道显示在“应用通知设置”里,让用户可以单独开关或调整声音/振动/优先级等。
- 如果相同 ID 的渠道已存在,系统不会随意覆盖用户已修改的设置(比如用户改变了重要性或声音),但应用可以通过再次调用
createNotificationChannel
更新可变字段(name、description 等)。重要性、声音等用户设置通常不会被覆盖。 - 这方法是幂等的——多次调用不会导致重复创建(对于同 ID,系统只会保持一个渠道)。
可以在手机应用设置中找到:
3. 使用通知
3.1 发送通知
Notification notification = new NotificationCompat.Builder(this, "my_channel_id")
.setSmallIcon(R.drawable.ic_launcher_background) // 小图标(必须有)
.setContentTitle("新消息提醒") // 标题
.setContentText("你有一条新的消息,请查收。") // 内容
.setPriority(NotificationCompat.PRIORITY_HIGH) // 优先级(7.1以下)
.setCategory(NotificationCompat.CATEGORY_MESSAGE) // 通知类别
.setWhen(System.currentTimeMillis()) // 时间戳
.build();
notificationManager.notify(1, notification);
3.2 更新通知
Notification notification = new NotificationCompat.Builder(this, "my_channel_id")
.setSmallIcon(R.drawable.ic_qq)
.setContentTitle("通知已更新")
.setContentText("newContent")
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setAutoCancel(true)
.build();
notificationManager.notify(1, notification); // 同 id 会覆盖之前的
3.3 取消通知
notificationManager.cancel(1); // 取消指定 id 的通知
这里主要有两个方法:notify()
和cancel()
notify —— 发送/更新通知
重载与参数:
notify(int id, Notification n)
notify(@Nullable String tag, int id, Notification n)
系统用 (tag, id) 作为唯一键:
- 只传
id
⇒ 等价于(null, id)
。 - 同一 (tag, id) 再次调用会覆盖/更新已有通知(而不是再叠一条)。
- 不同 id ⇒ 并列显示多条通知(常用于聊天多会话、下载多任务)。
建议:id 用稳定且可复现的数(如会话ID哈希);需要“堆叠多条”时用不同 id。
注意:
- 渠道(Android 8.0+):通知所属的 Channel 决定重要级别、声音、震动等;用户一旦改了渠道设置,你在代码里改
importance
也不会生效,除非创建新渠道 ID。 - 权限(Android 13+):没有
POST_NOTIFICATIONS
权限时,notify
调用不会崩但不会显示。可先用NotificationManagerCompat.areNotificationsEnabled()
检测。 - 小图标必须有:
setSmallIcon(...)
缺失会导致通知不显示/崩溃(部分系统)。 - 更新 vs 新发:要“更新”同一条,用同一个 (tag,id);要“多条并列”,用不同 id 或不同 tag。
- 前台服务:前台服务的通知由
startForeground(id, notification)
管理。要更新用notify(id, ...)
;要移除必须stopForeground(true)
(不要直接cancel(id)
试图偷撤)。 - ID 取值:
int
任意,但前台服务 id 需 > 0。不建议复用0
。 - 主线程调用:可以在主线程调用,但构建通知时的重资源操作(如大图 decode)最好放到后台,避免卡顿。
使用示例:
// 覆盖更新同一条:固定 id notificationManager.notify(1001, build("下载中 30%")); notificationManager.notify(1001, build("下载中 60%")); // 多条并列:不同 id notificationManager.notify(2001, build("张三:你好")); notificationManager.notify(2002, build("李四:在吗")); // 使用 tag 区分业务线 notificationManager.notify("chat-zhangsan", 1, build("张三:你好")); notificationManager.notify("chat-lisi", 1, build("李四:在吗"));
cancel —— 取消/移除通知
把已经显示(或排队)的某条通知移除。参数与
notify
一致,(tag, id) 必须匹配到同一条通知。
通知的进阶技巧
通知显示样式
Android 有一套“可扩展样式(expanded styles)”,可以让通知在下拉或通知抽屉中显示更多内容或图像。常用几种:
1. BigTextStyle — 显示大段文本(长消息)
setStyle(new NotificationCompat.BigTextStyle().bigText("这是很长的文字... \n\n\n\n可以完整显示多行内容"))
场景:长消息预览、日志摘要等。
2. BigPictureStyle — 显示大图
setStyle(new NotificationCompat.BigPictureStyle().bigPicture(BitmapFactory.decodeResource(getResources(), R.drawable.datu)))
注意:如果同时设置了 BigPictureStyle 与 BigTextStyle,某些设备/系统版本会以图片样式为主(长文可能不会全部展开)。如果你既要图又要长文本,需设计展示优先级或把长文本放在点击后进入的详情页。
3. InboxStyle — 多条汇总(像邮件列表)
setStyle(new NotificationCompat.InboxStyle().addLine("第一条消息").addLine("第二条消息").setSummaryText("+2 条"))
设备/系统版本会以图片样式为主(长文可能不会全部展开)。如果你既要图又要长文本,需设计展示优先级或把长文本放在点击后进入的详情页。
[外链图片转存中…(img-7ViMmcqh-1756631003433)]
3. InboxStyle — 多条汇总(像邮件列表)
setStyle(new NotificationCompat.InboxStyle().addLine("第一条消息").addLine("第二条消息").setSummaryText("+2 条"))