Android开发:如何正确绑定NotificationListenerService
Android开发:如何正确绑定NotificationListenerService
NotificationListenerService是Android提供的用于监听系统通知消息的服务类。在继承该类并重载相关方法后,即可在APP中读取和操作通知。(该部分过程不再赘述)
要使得NotificationListenerService获取的信息能够在APP中使用,需要在Fragment中实现与服务的通信。Android提供了三种方式:扩展 Binder 类、使用 Messenger、使用 AIDL。其中后两种方式都需要自行处理消息的传递,而扩展Binder类可以实现在Fragment或者Activity中直接调用服务的实例,在调用相关成员方法时较为方便。
为叙述方便清晰,假设自行编写的通知管理类为class MyNotiService:NotificationListenerService()
。
绑定服务常规操作
官方文档给出的扩展Binder类方法(在MyNotiService中定义):
|
|
此时,在需要绑定该服务的上下文(以Activity为例)中,需要使用ServiceConnection来管理服务的绑定情况,在Activity创建时需要通过发送Intent请求绑定服务,在Intent发出后便会调用服务类的OnBind方法返回IBinder。
注意,如果请求绑定和解绑写在onCreate和OnDestroy对,则在Activity存活时始终绑定;如果写在onStart和onStop,则只有在Activity前台运行时绑定服务。
|
|
上述绑定过程完成后,在Activity便可以直接调用mService这一实例及其成员。
错误、现象与分析
然而,如果将上述的代码应用于MyNotiService,会发现是不可行的。代码运行后将会发现OnListenerConnected方法将永远不会被调用,也就无法正常获取系统的通知。
其原因在于,NotificationListenerService属于需要请求系统的权限的特殊服务,在用户打开“通知管理权”页面授予通知读取权限的时刻,Android系统进程将会首先绑定MyNotiService服务。这一与系统的绑定是NotificationListenerService父类中定义的默认onBind方法实现的。
这也就是说,MyNotiService中要么不override onBind,要么就得显式调用父类的onBind使得能够正常与系统进程绑定,否则就会无法正常获取通知。
override fun onBind(intent: Intent): IBinder {
return super.onBind(intent)!!
}
经过上述分析,这时就处于一个尴尬的局面:如果与系统绑定,就无法返回自己的Binder;如果返回自己的Binder,就无法实现通知管理功能。
解决方法
最终在StackOverflow的这个问题下找到了解决方法。其原理是利用Intent可以携带额外信息的特点,通过一个简单的判断来确定发起绑定的是系统还是我们自己的Activity,然后返回正确的Binder。
例如,在我们自己的Activity中发送绑定Intent时,在extras中附带一个Boolean类型的值selfCall=true,表明绑定请求是从我们自己的程序发起的;
|
|
而在MyNotiService中,只需判断接收的参数Intent中是否有这个附加值,如果有,就返回自行扩展带Binder,否则就返回父类方法的返回值。
|
|
这样就可以在自己的Activity中绑定并调用MyNotiService,并且不影响正常的获取通知的功能了。
其他数据传递方案
单例模式(不可行)
在单例模式(Singleton)中,任何时候类至多只有一个实例,并且该实例可以通过调用类方法获取。除了使用object
代替`class``关键字外,还可以用下述方法实现单例模式:
|
|
在使用时,只需要调用SingletonDemo.get()
即可获得唯一实例。
乍一看,Android中的服务类在运行时也只具有一个实例,似乎十分适合采用单例模式。然而如果将上述单例模式的代码应用于MyNotiService,却会发现无法正常运行。
其原因是Android的Service不是普通的kotlin类,在field=SingletonDemo()
语句中创建的实例只是一个普通的kotlin对象,还不具有服务功能。
因此使用单例模式是无法实现在Activity中调用服务方法的。
静态成员变量/方法
对于一些常用的状态变量,也可以用静态成员变量通过MutableLiveData等类型传递。例如在服务中声明一个静态的Boolean成员,用于表示服务是否正在运行(LifecycleOwner是使用MutableLiveData所需的生命周期父类):
|
|
在Activity中,即可添加关于上述变量的监听事件:
|
|
这样就完成了简单的数据传递。
然而这种方法的缺点是,所有数据的更新都需要在MyNotiService内部手动处理,会增加许多冗余的更新代码。另外如果想要获取实例方法如activeNotifications
的返回值,只能通过一个中间变量存储,再用一个成员方法主动更新。这样的更新往往会滞后于通知的更新,而且也不一定在所有需要更新的时刻都有合适的回调。