Android 12.0 通知简介
https://blog.csdn.net/Smile_729day/article/details/135502031?spm=1001.2014.3001.5502 
1. 需求
在系统内置应用中或者在第三方应用中,获取Android系统收到的通知的内容.
2. NotificationListenerService 接口
Android 系统预留了专门的API, 即 NotificationListenerService 接口,它的源码路径为:
源码路径 : /frameworks/base/core/java/android/service/notification/NotificationListenerService.java
public abstract class NotificationListenerService extends Service {
              ...... 
 
3. 实现 NotificationListenerService
NotificationListenerService 是抽象类,通过在 Service 中实现该抽象类,并实现需要的接口,代码如下:
public class MyNotificationListenerService extends NotificationListenerService {
    //当系统收到新的通知时,会触发该接口
    @Override
    public void onNotificationPosted(StatusBarNotification sbn) {
       
     addAlienNotificationInfo(sbn); //获取通知的内容
    }
    //当系统移除通知时,即在通知列表中清除通知时,或者卸载应用时,该应用包名下的通知都会被清除,也同样会会触发该接口
    @Override
    public void onNotificationRemoved(StatusBarNotification sbn) {
        super.onNotificationRemoved(sbn);
    }
} 
上面两个接口,第一个是监听系统新通知,第二个是监听系统通知清除,如果看过Android Systemui 中有关通知的内容,就会发现,System UI 对通知的显示和通知的清除,同样也是继承该API.
下面,分析当系统收到新通知时,如何解析出通知里的内容.
4. 获取通知内容
   private void addAlienNotificationInfo(StatusBarNotification sbn) {
        String packageName = sbn.getPackageName();//获取发送通知的包名
        
        Notification notification = sbn.getNotification(); //获得一个Notification对象
        Bundle extras = notification.extras;
        RemoteViews contentView = notification.contentView;
        //ApplicationInfo appInfo = extras.getParcelable(NotificationCompat.EXTRA_BUILDER_APPLICATION_INFO);//如果是系统内置应用,可以获取到ApplicationInfo ,后面会有解释
        //String appName = appInfo.loadLabel(mPm).toString(); //如果是系统内置的应用,可以通过ApplicationInfo对象获取通知发送这条通知的应用名
        String category = notification.category;
        String channelId = notification.getChannelId();
        //String className = getNotificationClassName(notification);//如果是系统内置应用,可以获取到通知中设置的Intent,后面会有解释
        int color = notification.color;//通知setColor()设置的颜色
        boolean defaultVibrate = (notification.vibrate == null) ? true : false;
        String notificationVibrationString = Arrays.toString(notification.vibrate);
        int importance = notification.priority; //通知的重要性
        
        String key = sbn.getKey();//通知的Key,删除通知时,需要通过Key来确定删除哪条通知
        Icon myLargeIconToIcon = extras.getParcelable(Notification.EXTRA_LARGE_ICON);//获取通知设置的大图片,即setLargeIcon() ,
        
        int ledColor = notification.ledARGB;//通知led灯颜色
        String sound = (notification.sound != null) ? notification.sound.toString() : null;
        int progress = extras.getInt(Notification.EXTRA_PROGRESS);//当前进度值
        int progressMax = extras.getInt(Notification.EXTRA_PROGRESS_MAX);//设定的最大进度值
        boolean progressIndeterminate = extras.getBoolean(Notification.EXTRA_PROGRESS_INDETERMINATE);//是否在通知中显示进度值
        int flagsToCheck = Notification.FLAG_ONGOING_EVENT | Notification.FLAG_NO_CLEAR;
        boolean resident = (notification.flags & flagsToCheck) != 0;//是否是常驻通知(前台&onging),类似于音乐应用,
        String smallIcon = notification.getSmallIcon().toString();//通知设置的smallIcon()
        String title = (String) extras.get(Notification.EXTRA_TITLE);//通知标题
        String subText = (String) extras.get(Notification.EXTRA_SUB_TEXT);//通知附加内容
        String text = (String) extras.get(Notification.EXTRA_TEXT);//通知内容
        boolean userRemovable = (notification.flags & Notification.FLAG_AUTO_CANCEL) != 0; //是否可移除通知,即 setAutoCancel(boolean autoCancel) 中设定的值
        long when = notification.when;//通知的时间
        String template = getNotificationTemplate(notification);//获取通知的类型,下面会详细介绍
        //BigTextStyle通知中,bigText的内容
        String bigText = (String) extras.getCharSequence(Notification.EXTRA_BIG_TEXT);    
        //BigPictureStyle通知中设置的大图片
        Bitmap picture_extraBitmap = extras.getParcelable(Notification.EXTRA_PICTURE);
        //状态栏通知列表中,折叠时是否显示图片,注意:该属性在Android12上有,Android11上没有,
        boolean mShowBigPictureWhenCollapsed = extras.getBoolean(Notification.EXTRA_SHOW_BIG_PICTURE_WHEN_COLLASED);
       //通过下面的方法也可以查看通知中的设置的全部参数
        for(String mykey : notification.extras.keySet()){
            String ex = " " + mykey +" => " + notification.extras.get(mykey)+ ";";
            Log.d(TAG,"ex="+ex);
        }
} 
(1) 获取ApplicationInfo
上面在获取通知的ApplicationInfo时,使用了 Notification.EXTRA_BUILDER_APPLICATION_INFO,该值不对系统外开放,如下源码所示:
源码路径:/frameworks/base/core/java/android/app/Notification.java
 
/**
     * @hide
     */
    public static final String EXTRA_BUILDER_APPLICATION_INFO = "android.appInfo"; 
(2) getNotificationClassName(notification)
上面还涉及到了getNotificationClassName(notification) 获取这条通知中通过PendingIntent中设置的 Intent ,方法中用到了不对外使用的方法,代码如下:
 private String getNotificationClassName(Notification notification) {
        if (notification.contentIntent != null) {
            Intent intent = notification.contentIntent.getIntent();
            if (intent != null && intent.getComponent() != null) {
                return intent.getComponent().getClassName();
            }
        }
        return null;
    } 
(3) 获取 PendingIntent 对象
其中的 notification.contentIntent 是获取 通知中的 PendingIntent 对象,源码如下:
源码路径:/frameworks/base/core/java/android/app/Notification.java 
/**
     * 单击展开的状态条目时要执行的意图。
     */
    public PendingIntent contentIntent; 
(4) PendingIntent.getIntent()
接着再通过调用 PendingIntent 对象中的 getIntent() 来获取通知中设定的 Intent, 源码如下:
源码路径: /frameworks/base/core/java/android/app/PendingIntent.java
 /**
     * @hide (该方法不对外公开)
     * 返回 PendingIntent 中的 Intent .
     */
    @UnsupportedAppUsage
    public Intent getIntent() {
        try {
            return ActivityManager.getService()
                .getIntentForIntentSender(mTarget);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    } 
由于源码中有@hide,表明该方法不对第三方应用开放,所以如果是系统内置的应用,则可以使用.
(5) getNotificationTemplate(Notification notification)
Android中定义了一些通知模板,常用的如: BigPictureStyle , BigTextStyle 等,代码如下:
/**
 * 返回通知的类型,如果没有使用,则返回null.
 * 返回的类型,如: "android.app.Notification$BigTextStyle",
 * 因此需要对字符串进行截取,如最后返回: "BigTextStyle"
*/
private String getNotificationTemplate(Notification notification) {
        String template = notification.extras.getString(Notification.EXTRA_TEMPLATE);
        if (template != null) {
            int indexOf = template.indexOf('$');
            return template.substring(indexOf + 1);
        }
        return null;
    } 
 
5. 通知的清除
当通知被清除时,会调用 onNotificationRemoved(StatusBarNotification sbn) 方法,其中的参数 sbn 代表被删除的通知.可以在该方法里做一些删除后的工作.
6 . NotificationListenerService 中其他有用的方法
(1) 获取有效的通知 : getActiveNotifications()
源码路径: /frameworks/base/core/java/android/service/notification/NotificationListenerService.java
/**
     * 请求未完成通知的列表(即那些对当前用户)。
     *
     * @return 活动通知数组,按自然顺序排序。
     */
    public StatusBarNotification[] getActiveNotifications() {
        StatusBarNotification[] activeNotifications = getActiveNotifications(null, TRIM_FULL);
        return activeNotifications != null ? activeNotifications : new StatusBarNotification[0];
    } 
(2) 删除指定单条通知 : cancelNotification(String key)
源码路径: /frameworks/base/core/java/android/service/notification/NotificationListenerService.java 
/**
     * 删除指定的一条通知
     *
     */
    public final void cancelNotification(String key) {
        if (!isBound()) return;//是否绑定了NotificationListenerService服务
        try {
            getNotificationInterface().cancelNotificationsFromListener(mWrapper,
                    new String[] { key });
        } catch (android.os.RemoteException ex) {
            Log.v(TAG, "Unable to contact notification manager", ex);
        }
    } 
(3) 删除指定的多条通知: cancelNotifications(String[] keys)
源码路径: /frameworks/base/core/java/android/service/notification/NotificationListenerService.java 
/**
     * 删除 数组 keys 中指定key的通知
     */
    public final void cancelNotifications(String[] keys) {
        if (!isBound()) return;
        try {
            getNotificationInterface().cancelNotificationsFromListener(mWrapper, keys);
        } catch (android.os.RemoteException ex) {
            Log.v(TAG, "Unable to contact notification manager", ex);
        }
    }
 
(4) 清除所有通知,对应于通知列表下的 清除所有通知的按钮功能: cancelAllNotifications()
  /**
     * 通知通知管理器清除所有通知
     * 类似于Android状态栏和通知面板调用 UI 的“全部关闭”功能
     * 收到通知后,通知管理器实际上会删除所有活动通知
     * 并且收到多个 {@link #onNotificationRemoved(StatusBarNotification)} 回调。
     */
    public final void cancelAllNotifications() {
        cancelNotifications(null /*all*/);
    } 
至此,关于监听系统通知介绍完毕,谢谢观看!

![[R] Why data manipulation is crucial and sensitive?](https://img-blog.csdnimg.cn/direct/4718e637586c4cc5aaf05074c65a8885.png)

















