19、通知的广播监听:监听通知的发送、监听通知的取消、NotificationListenerService的使用
通知这玩意儿,说白了就是系统和用户之间的传话筒。但有时候,我们不光要发通知,还得知道别人发了什么通知、什么时候被取消了。嗯,这就是广播监听的用武之地。
我个人习惯把通知监听分成两类:一类是被动监听,通过广播接收器来捕获系统发出的通知相关广播;另一类是主动监听,通过 NotificationListenerService 来实时获取通知的流。今天咱们就把这两条路都走一遍。
19.1 监听通知的发送与取消(广播方式)
Android 系统在通知被发送或取消时,会发送一些隐式广播。不过我得先泼一盆冷水:从 Android 8.0 开始,大部分隐式广播都被限制了。你想想看,如果每个 App 都能随便监听别人的通知,那隐私不就成笑话了吗?
但好消息是,系统还是留了几个口子。比如 NotificationManager.ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED 这个广播,在用户授权或取消通知监听权限时会触发。不过说实话,这个广播用途有限,真正干活还得靠 NotificationListenerService。
那有没有别的办法?有,但比较取巧。你可以通过 ContentObserver 监听通知相关的 Settings 数据库变化。不过这种方式不够实时,而且需要 SYSTEM_ALERT_WINDOW 权限,我个人不太推荐。
19.2 NotificationListenerService:真正的通知监听利器
如果你真的需要监听通知的发送和取消,NotificationListenerService 是官方推荐的正规军。它可以在通知出现、更新、消失时回调你,而且不需要轮询,性能开销很小。
我记得第一次用这个服务时,踩了个大坑——用户必须手动授权。没错,这个服务需要用户在系统设置里开启「通知访问权限」。你想想看,如果随便一个 App 都能监听你的通知,那微信消息不就全暴露了吗?
19.2.1 基本使用步骤
- 创建一个继承自
NotificationListenerService的类 - 在清单文件中声明服务,并请求
BIND_NOTIFICATION_LISTENER_SERVICE权限 - 重写
onNotificationPosted()和onNotificationRemoved()方法 - 引导用户开启通知访问权限
来看代码:
// 1. 创建服务类
public class MyNotificationListener extends NotificationListenerService {
@Override
public void onNotificationPosted(StatusBarNotification sbn) {
// 通知被发送或更新时回调
String packageName = sbn.getPackageName();
String tag = sbn.getTag();
int id = sbn.getId();
Notification notification = sbn.getNotification();
Log.d("NotifListener", "收到通知来自: " + packageName);
Log.d("NotifListener", "通知标题: " + notification.extras.getString(
Notification.EXTRA_TITLE));
Log.d("NotifListener", "通知内容: " + notification.extras.getString(
Notification.EXTRA_TEXT));
}
@Override
public void onNotificationRemoved(StatusBarNotification sbn) {
// 通知被取消时回调
String packageName = sbn.getPackageName();
Log.d("NotifListener", "通知被移除: " + packageName);
}
}
<!-- 2. 清单文件声明 -->
<service
android:name=".MyNotificationListener"
android:label="通知监听服务"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
<intent-filter>
<action android:name="android.service.notification.NotificationListenerService" />
</intent-filter>
</service>
19.2.2 引导用户授权
这个步骤很容易被忽略。我曾经有个项目上线后,发现大部分用户的通知监听功能都没生效,排查了半天才发现是权限没开。所以一定要在代码里引导用户去设置页面开启。
// 3. 检查并引导授权
private void checkNotificationListenerPermission() {
String packageName = getPackageName();
String flat = Settings.Secure.getString(
getContentResolver(),
"enabled_notification_listeners"
);
if (flat != null && flat.contains(packageName)) {
// 已经授权
return;
}
// 跳转到设置页面
startActivity(new Intent(
Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS
));
}
onNotificationPosted() 里加一个判断,只处理特定包名的通知,避免日志刷屏。比如只监听微信或支付宝的通知,这样既省电又清晰。
19.3 核心知识体系
下面这张图是我自己总结的通知监听知识结构,你看一眼就能明白各个组件之间的关系:
19.4 实战中的坑与技巧
在实际项目中,我遇到过几个比较典型的问题,分享给你:
- 服务被系统杀死:NotificationListenerService 是一个后台服务,系统在内存不足时可能会把它杀掉。我建议在
onDestroy()里打印日志,方便排查。如果发现服务频繁被杀,可以考虑提高进程优先级,或者使用startForeground()。 - 通知内容获取不全:有些 App 的通知使用了自定义布局,通过
notification.extras可能拿不到标题和内容。这时候可以尝试notification.bigContentView或notification.headsUpContentView,但说实话,这属于逆向工程了,不太推荐。 - 权限被用户关闭:用户随时可能去设置里关掉通知监听权限。我习惯在
onListenerConnected()和onListenerDisconnected()里做状态记录,并在 UI 上提示用户。
- 广播监听通知的方式在 Android 8.0 之后基本失效,别在这上面浪费时间
- NotificationListenerService 是唯一可靠的通知监听方案
- 用户授权是必须的,记得在代码里引导
- 服务可能被系统杀死,要做好重连和状态恢复
嗯,关于通知的广播监听,核心内容就这些。说白了,就是两条路:广播那条路已经被堵死了,老老实实用 NotificationListenerService 吧。我刚开始学的时候也纠结过广播方案,后来发现官方文档写得明明白白——用服务,别用广播。
你想想看,通知监听这种涉及用户隐私的功能,系统怎么可能让一个广播接收器就搞定?所以 NotificationListenerService 的设计是合理的,虽然多了一步授权流程,但换来了更稳定、更安全的监听体验。
公众号:蓝海资料掘金营,微信deep3321