19、通知的广播监听:监听通知的发送、监听通知的取消、NotificationListenerService的使用

通知这玩意儿,说白了就是系统和用户之间的传话筒。但有时候,我们不光要发通知,还得知道别人发了什么通知、什么时候被取消了。嗯,这就是广播监听的用武之地。

我个人习惯把通知监听分成两类:一类是被动监听,通过广播接收器来捕获系统发出的通知相关广播;另一类是主动监听,通过 NotificationListenerService 来实时获取通知的流。今天咱们就把这两条路都走一遍。

19.1 监听通知的发送与取消(广播方式)

Android 系统在通知被发送或取消时,会发送一些隐式广播。不过我得先泼一盆冷水:从 Android 8.0 开始,大部分隐式广播都被限制了。你想想看,如果每个 App 都能随便监听别人的通知,那隐私不就成笑话了吗?

但好消息是,系统还是留了几个口子。比如 NotificationManager.ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED 这个广播,在用户授权或取消通知监听权限时会触发。不过说实话,这个广播用途有限,真正干活还得靠 NotificationListenerService。

注意: 我曾经在 Android 9 的项目里试图用静态广播接收器监听通知的发送,结果发现根本收不到。后来查了文档才明白,从 Android 8.0 开始,系统不再允许清单文件中静态注册的隐式广播接收器。所以这条路基本走不通了。

那有没有别的办法?有,但比较取巧。你可以通过 ContentObserver 监听通知相关的 Settings 数据库变化。不过这种方式不够实时,而且需要 SYSTEM_ALERT_WINDOW 权限,我个人不太推荐。

19.2 NotificationListenerService:真正的通知监听利器

如果你真的需要监听通知的发送和取消,NotificationListenerService 是官方推荐的正规军。它可以在通知出现、更新、消失时回调你,而且不需要轮询,性能开销很小。

我记得第一次用这个服务时,踩了个大坑——用户必须手动授权。没错,这个服务需要用户在系统设置里开启「通知访问权限」。你想想看,如果随便一个 App 都能监听你的通知,那微信消息不就全暴露了吗?

19.2.1 基本使用步骤

  1. 创建一个继承自 NotificationListenerService 的类
  2. 在清单文件中声明服务,并请求 BIND_NOTIFICATION_LISTENER_SERVICE 权限
  3. 重写 onNotificationPosted()onNotificationRemoved() 方法
  4. 引导用户开启通知访问权限

来看代码:

// 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 核心知识体系

下面这张图是我自己总结的通知监听知识结构,你看一眼就能明白各个组件之间的关系:

通知监听知识体系 系统通知 广播方式(受限) NotificationListenerService 隐式广播限制(Android 8.0+) 需要用户手动授权 onNotificationPosted / Removed

19.4 实战中的坑与技巧

在实际项目中,我遇到过几个比较典型的问题,分享给你:

  • 服务被系统杀死:NotificationListenerService 是一个后台服务,系统在内存不足时可能会把它杀掉。我建议在 onDestroy() 里打印日志,方便排查。如果发现服务频繁被杀,可以考虑提高进程优先级,或者使用 startForeground()
  • 通知内容获取不全:有些 App 的通知使用了自定义布局,通过 notification.extras 可能拿不到标题和内容。这时候可以尝试 notification.bigContentViewnotification.headsUpContentView,但说实话,这属于逆向工程了,不太推荐。
  • 权限被用户关闭:用户随时可能去设置里关掉通知监听权限。我习惯在 onListenerConnected()onListenerDisconnected() 里做状态记录,并在 UI 上提示用户。
核心要点:
  • 广播监听通知的方式在 Android 8.0 之后基本失效,别在这上面浪费时间
  • NotificationListenerService 是唯一可靠的通知监听方案
  • 用户授权是必须的,记得在代码里引导
  • 服务可能被系统杀死,要做好重连和状态恢复

嗯,关于通知的广播监听,核心内容就这些。说白了,就是两条路:广播那条路已经被堵死了,老老实实用 NotificationListenerService 吧。我刚开始学的时候也纠结过广播方案,后来发现官方文档写得明明白白——用服务,别用广播

你想想看,通知监听这种涉及用户隐私的功能,系统怎么可能让一个广播接收器就搞定?所以 NotificationListenerService 的设计是合理的,虽然多了一步授权流程,但换来了更稳定、更安全的监听体验。


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