该文转载自:http://blog.csdn.net/yihongyuelan
强烈建议阅读原文
概况
Android在4.3的版本中(即API 18)加入了NotificationListenerService,根据SDK的描述(AndroidDeveloper)可以知道,当系统收到新的通知或者通知被删除时,会触发NotificationListenerService的回调方法。同时在Android 4.4 中新增了Notification.extras 字段,也就是说可以使用NotificationListenerService获取系统通知具体信息,这在以前是需要用反射来实现的。
重要关系
对于系统通知,三方APP使用NotificationListenerService主要目的是为了获取系统通知相关信息,主要包括:通知的新增和删除,获取当前通知数量,通知内容相关信息等。这些信息可以通过NotificationListenerService类提供的方法以及StatusBarNotification类对象来获取。
NotificationListenerService主要方法(成员变量):
cancelAllNotifications() :删除系统中所有可被清除的通知;
cancelNotification(String pkg, String tag, int id) :删除具体某一个通知;
getActiveNotifications() :返回当前系统所有通知到StatusBarNotification[];
onNotificationPosted(StatusBarNotification sbn) :当系统收到新的通知后出发回调;
onNotificationRemoved(StatusBarNotification sbn) :当系统通知被删掉后出发回调;
以上是NotificationListenerService的主要方法,通过这些方法就可以在应用中操作系统通知,在NotificationListenerService中除了对通知的操作之外,还可以获取到通知的StatusBarNotification对象,通过该对象可以获取通知更详细的数据。
StatusBarNotification主要方法(成员变量):
getId():返回通知对应的id;
getNotification():返回通知对象;
getPackageName():返回通知对应的包名;
getPostTime():返回通知发起的时间;
getTag():返回通知的Tag,如果没有设置返回null;
getUserId():返回UserId,用于多用户场景;
isClearable():返回该通知是否可被清楚,FLAG_ONGOING_EVENT、FLAG_NO_CLEAR;
isOngoing():检查该通知的flag是否为FLAG_ONGOING_EVENT;
使用简介
正确使用NotificationListenerService需要注意三点:
(1). 新建一个类并继承自NotificationListenerService,override其中重要的两个方法;
(2). 在AndroidManifest.xml中注册Service并声明相关权限;
使用详解
通过前面的讲解(实际上就是对AndroidDeveloper的解释),已经可以正常使用NotificationListenerService了,但对于实际应用中,需要考虑的事情还比较多。比如:
1. 如何检测应用已开启Notification access监听功能?
如果检测到应用没有激活Notification access监听功能,需要提示用户开启;
2. 能不能主动跳转到Notification access监听页面?
如果能够根据第1步的判断自动跳转到对应的页面,那可以省掉很多操作;
3. 如何与NotificationListenerService交互?
涉及到与Service的交互,但又与普通的Service不同,这里后文解释;
4. NotificationListenerService使用过程中有哪些注意事项?
在使用NotificationListenerService过程中自己遇到了一些坑,后文会通过分析给出相应的解决方案;
功能分析
1. 如何检测应用已开启Notification access监听功能?
在程序启动时,执行Notification access的检测,查看是否访问Notification的权限。如果用户没有Enable Notification access,则弹出提示对话框,点击OK跳转到Notification access设置页面。
使用NotificationListenerService的应用如果开启了Notification access,系统会将包名等相关信息写入SettingsProver数据库中,因此可以从数据库中获取相关信息并过滤,从而判断应用是否开启了Notification access,代码如下:
在返回值flat中如果包含了应用的包名,即可确定应用已开启Notification access,反之则表示没有开启。2. 能不能主动跳转到Notification access监听页面?
通过查看可以知道,Notification access界面接收action为"android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS"的intent启动,因此使用startActivity可以很容易的跳转到该页面,从而避免用户在Settings中查找。代码如下:
3. 如何与NotificationListenerService交互?
因为NotificationListenerService中包含了四个重要的方法,分别是:onNotificationPosted、onNotificationRemoved、cancelNotification、cancelAllNotifications。通过这些方法我们才能实现诸如通知信息的获取以及删除等功能,虽然这些方法是public的,那是不是意味着我们只要拿到NotificationListenerService的对象就可以直接调用这些方法了呢?那如何拿到Service的对象呢?在之前的博文中,曾有提到与Service的交互( 具体可参考拙作《Android中程序与Service交互的方式——交互方式》),可以看到与Service的交互有很多种方法,但如果要拿到Service的对象,归根到底还是需要Binder。
也就是说得使用bindService的办法,将onServiceConnected回调中的IBinder对象转型成NotificationListenerService的对象。测试代码如下:
而NotificationMonitor继承自NotificationListenerService,默认的onBind方法却是:
这里注意,一般情况下service的onBind方法返回要么是null要么是Binder对象,可这里直接调用父类NotificationListenerService的onBind方法,而父类返回的是INotificationListenerWrapper的对象。这说明Binder对象已经被指定了,不能再给NotificationMonitor指定其它的Binder对象。如果你非要给NotificationMonitor指定其它的Binder对象,那么就无法使用INotificationListenerWrapper提供的方法。也就是说要么就用系统NotificationListenerService提供的方法,要么就把NotificationMonitor当一个普通的Service来用,系统提供的方法都不能使用。那应该如何使用NotificationListenerService中的方法呢?在拙作《Android中程序与Service交互的方式——交互方式》中,已经提供了很多的例子,这里仅以广播的方式为例。
既然NotificationMonitor可以使用NotificationListenerService的方法,那通过NotificationMonitor把通知状态的改变以及数据获取到,并使用static数据进行存储,之后再在MainActivity中直接使用即可。在MainActivity中控制通知的单个删除和全部删除,则使用广播的方式发送给NotificationMonitor进行处理。MainActivity与NotificationMonitor的关系类图如下:
4. NotificationListenerService使用过程中有哪些注意事项?
如果细心察看代码的童鞋,一定发现代码中有使用Handler,以及一些奇怪但又被注释掉的代码,比如"a.equals("b")"。从使用上来说,没有必要使用handler,那干嘛要多次一举?这里就给大家分享一下在写NotificationListenerDemo时遇到的一些坑。
①. NotificationMonitor的onCreate方法中使用handler来调用getActiveNotifications()方法
若直接在onCreate或者onBind方法中调用getActiveNotifications()方法是无法获取当前系统通知。主要是因为NotificationMonitor还未完成初始化,而根本原因则是INotificationListenerWrapper对象mWrapper还未初始化,此时使用getActiveNotifications()方法又会调用到mWrapper,因此无法返回正常数据。在NotificationListenerService中可以看到getActiveNotifications()的源码:
也就是说只要在onBind方法完成之后,再调用getActiveNotifications()方法就可以正常获取数据了,因此这里使用了handler多线程的方式。当然,为了保险可以使用sendEmptyMessgeDelay加上延时。②. 如果NotificationMonitor在onCreate或onBind方法中crash,则该service已经失效,需重启手机才能进行后续开发验证
如果在onCreate或者onBind方法中,出现异常导致NotificationMonitor发生crash,就算找到问题并将其改正,之后的验证还是无法继续进行的,也就是无法收到通知的新增和删除消息,onNotificationPosted和onNotificationRemoved方法不会被调用。
这也是我在onBind方法中故意注释导致空指针异常的代码,有兴趣的童鞋可以把注释去掉后尝试,去掉注释会导致NotificationListenerDemo异常停止,此时你再加上注释再次运行NotificationListenerDemo,虽然程序可以正常启动,但无法正常执行NotificationMonitor中的onNotificationPosted和onNotificationRemoved方法。这个涉及NotificationListenerService的原理,后面会另行分析。
③. MainActivity中onClick方法里使用handler操作
当点击删除通知时,系统通知相关状态还未更新,此时还没有回调到NotificationMonitor中,所以获取的数据就还是上一次的数据。为了能够获取到正确的Notification数据,可以使用handler并加上延时,这样再去获取Notification信息时,系统已经触发了NotificationMonitor回调,数据也有正常了。另外,50ms的延时几乎是感知不到的。
④. 为什么要使用ArrayList来保存StatusBarNotification数组对象
当新增或者删除通知时,会触发onNotificationPosted或onNotificationRemoved回调,在该方法中调用getActiveNotifications()方法用以获取当前系统通知信息。而getActiveNotifications()返回的是StatusBarNotification[]数组,因为这个数组是可变长的,也就是长度会随时变化,因此无法直接存储。使用ArrayList可以很好的解决这个问题,在ArrayList对象中添加一个StatusBarNotification[]对象,之后使用ArrayList.set(0,statusbar[])方法对数据进行更新即可。
总结
NotificationListenerService是Android 4.3 之后新增的接口服务,用于获取系统Notification信息,这在之前的Android版本是无法直接办到的。在Android 4.4中,增加了Notification.extra变量,使得获取Notification相关信息更加丰富,这些接口的开放更加利于三方应用的使用,但同时也会带来一些隐私问题。
本文针对NotificationListenerService的使用进行了详细分析,当然其中不乏有失偏颇的地方,本着互联网知识共享精神也将自己的一些记录发布出来,一来可做笔记,二来希望能够给苦苦寻觅的童鞋一些帮助。
后续会对NotificationListenerService的原理进行分析,敬请期待。
demo地址:https://github.com/yihongyuelan/NotificationListenerServiceDemo
官方地址介绍:https://developer.android.com/reference/android/service/notification/NotificationListenerService.html
只有适合自己的才是最好的