26、通知的性能优化:通知的复用、避免频繁更新、批量操作、通知的内存管理
通知这东西,看起来简单,不就是发个弹窗嘛。但你要是做过日活千万级的App,就知道通知优化有多重要了。我见过不少App,通知发着发着就卡了,甚至直接OOM崩溃。说白了,通知管理不当,就是一颗定时炸弹。
今天咱们聊聊通知的性能优化。四个方向:复用、避免频繁更新、批量操作、内存管理。嗯,一个一个来。
通知的复用:别每次都新建
你想想看,每次发通知都创建一个新的Notification对象,这合理吗?不合理。尤其是那些持续更新的通知,比如音乐播放器、下载进度条、导航提示——这些场景下,通知的内容在变,但通知本身是同一个。
我个人习惯的做法是:使用相同的通知ID。只要ID相同,系统就会自动复用之前的通知,而不是创建新的。
// 错误示范:每次都新建
NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
for (int i = 0; i < 10; i++) {
Notification notification = buildNotification("进度:" + i + "%");
manager.notify(i, notification); // 每次ID都不同,创建了10个通知
}
// 正确做法:复用同一个ID
NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
int NOTIFICATION_ID = 1001;
for (int i = 0; i < 10; i++) {
Notification notification = buildNotification("进度:" + i + "%");
manager.notify(NOTIFICATION_ID, notification); // 同一个ID,复用通知
}
核心原则:同一个逻辑通知,永远使用同一个ID。不要为了更新内容就换ID。
我在项目中遇到过一个问题:音乐播放器的通知,每次切歌都换ID。结果通知栏里堆了一堆播放器通知,用户都懵了。后来改成固定ID,一切正常。
避免频繁更新:别让系统累死
有些场景下,通知更新的频率非常高。比如下载进度条,每秒更新几十次。你想想看,每次更新都要走一遍Binder跨进程通信,系统不累吗?
我的建议是:做节流。说白了,就是控制更新频率,不要每次变化都去调notify()。
// 节流更新:至少间隔200ms
private long lastUpdateTime = 0;
private static final long THROTTLE_MS = 200;
public void updateProgress(int progress) {
long now = System.currentTimeMillis();
if (now - lastUpdateTime < THROTTLE_MS) {
return; // 更新太频繁,跳过
}
lastUpdateTime = now;
Notification notification = buildProgressNotification(progress);
notificationManager.notify(NOTIFICATION_ID, notification);
}
小技巧:对于进度条通知,我一般控制在每秒更新5次以内。人眼根本看不出每秒30帧和每秒5帧的区别,但系统负担能减少80%。
为什么会这样?因为每次notify()都会触发系统重新渲染通知栏。你更新得越快,系统UI线程就越忙,最终导致通知栏卡顿,甚至ANR。
批量操作:一次搞定别零碎
有时候你需要同时更新多个通知,或者取消多个通知。如果你一个一个来,每次都是一次跨进程调用。批量操作就是把这些调用合并成一次。
Android从API 14开始就支持了批量操作,但很多人不知道。嗯,这里要注意:使用cancelAll()而不是逐个cancel()。
// 错误示范:逐个取消
for (int id : notificationIds) {
notificationManager.cancel(id); // 每个ID一次跨进程调用
}
// 正确做法:批量取消
notificationManager.cancelAll(); // 一次调用,取消所有通知
// 或者使用NotificationManager的cancelAsPackage(需要权限)
// 但一般用cancelAll就够了
批量更新的场景呢?比如你一次性收到了10条消息,每条都要发通知。这时候不要循环调10次notify(),而是应该合并成一条通知(使用通知分组功能)。
// 使用通知分组:合并多条消息
Notification summary = new NotificationCompat.Builder(this, CHANNEL_ID)
.setGroup(GROUP_KEY_MESSAGES)
.setGroupSummary(true)
.setContentTitle("你有10条新消息")
.setContentText("点击查看详情")
.build();
notificationManager.notify(SUMMARY_ID, summary);
我曾经踩过的坑:有一次批量发送通知,循环里调了50次notify(),结果低端手机上直接卡死。后来改成分组通知,性能问题瞬间解决。
通知的内存管理:别让通知吃光内存
通知本身是跨进程的,Notification对象会被序列化后传给SystemServer。如果你在Notification里塞了太多东西,比如大图、长文本、自定义RemoteViews,那内存消耗就很可观了。
我见过最夸张的情况:有人在Notification的extras里塞了一个2MB的Bitmap。结果每次更新通知,都要序列化2MB的数据跨进程传输。你说系统能不卡吗?
| 通知内容 | 内存消耗(估算) | 优化建议 |
|---|---|---|
| 纯文本通知 | ~5KB | 无需优化 |
| 带小图标(48x48) | ~50KB | 使用矢量图或压缩PNG |
| 带大图(720x1280) | ~500KB-2MB | 压缩到合理尺寸,使用BitmapFactory.Options |
| 自定义RemoteViews | ~100KB+ | 减少View层级,避免复杂布局 |
优化内存的几个实操建议:
- 图片压缩:通知里的大图,一定要压缩。我一般控制在720px宽度以内,质量80%就够了。
- 避免大对象:不要在Notification的extras里放Bitmap、大字符串、序列化对象。
- 及时清理:不再需要的通知,记得cancel掉。别让通知栏里堆着几十条历史通知。
- RemoteViews轻量化:自定义布局尽量简单,不要嵌套太多层级。
// 图片压缩示例
public Bitmap compressForNotification(Bitmap original) {
int maxWidth = 720;
int width = original.getWidth();
int height = original.getHeight();
if (width <= maxWidth) {
return original;
}
float ratio = (float) maxWidth / width;
int newWidth = maxWidth;
int newHeight = (int) (height * ratio);
return Bitmap.createScaledBitmap(original, newWidth, newHeight, true);
}
一句话总结:通知的内存管理,核心就是「少传数据、及时释放」。别把通知当成数据通道,它只是个UI组件。
知识体系总览
下面这张图,把通知性能优化的四个维度串起来了。你可以把它当作一个检查清单,每次做通知功能时对照一下。
这四个维度,说白了就是一句话:别让通知成为性能瓶颈。复用ID、控制频率、批量操作、管好内存——做到这四点,你的通知功能基本就稳了。
我做了这么多年Android,见过太多通知相关的性能问题。其实大部分问题都不是技术难题,而是习惯问题。养成好习惯,通知优化就是顺手的事。