《Android 4.4 KitKat NotificationManagerService使用详解与原理分析(一)__使用详解》java
转载请务必注明出处:http://blog.csdn.net/yihongyuelan
android
在上一篇文章《Android 4.4 KitKat NotificationManagerService使用详解与原理分析(一)__使用详解》中详细介绍了NotificationListenerService的使用方法,以及在使用过程当中遇到的问题和规避方案。本文主要分析NotificationListenerService实现原理,以及详细分析在上一篇文章中提到的相关问题和产生的根本缘由。在原理分析前,先看看NotificationListenerService涉及到的类以及基本做用,如图1所示:git
图 1 NLS注册及回调过程github
经过图1能够看到,整个通知状态获取分为三部分:数据库
①. 监听器注册;新建一个类NotificationMonitor继承自NotificationListenerService。app
②. 系统通知管理;系统通知管理由NotificationManagerService负责。异步
③. 通知状态回调;当系统通知状态改变以后,NotificationManagerService会通知NotificationListenerService,最后再由NotificationListenerService通知其全部子类。ide
在整个系统中,通知管理是由NotificationManagerService完成的,NotificationListenerService只是在通知改变时,会得到相应的通知消息,这些消息最终会回调到NotificationListenerService的全部子类中。post
NotificationListenerService虽然继承自Service,但系统中实际上启动的是其子类,为了表述方便,后文统一使用NotificationListenerService启动来指代。其子类的启动有三个途径,分别是:开机启动、接收PACKAGE相关广播(安装、卸载等)启动、SettingsProvider数据变动启动。this
既然NotificationListenerService是一个service,那其子类启动方式天然就是bindService或者startService,在SourceCode/frameworks/base/services/java/com/android/server/NotificationManagerService.java中能够找到,实际上NotificationListenerService的启动是经过bindServiceAsUser来实现的,而bindServiceAsUser与bindService做用一致。
由于NotificationListenerService最终是在NotificationManagerService中启动的,所以当系统在开机第一次启动时,会进行NotificationManagerService初始化,以后会调用其SystemReady方法,继而调用rebindListenerServices以及registerListenerService(),最后使用bindServiceAsUser实现NotificationListenerService的启动。rebindListenerServices代码以下:
[java] view plaincopy
void rebindListenerServices() {
final int currentUser = ActivityManager.getCurrentUser();
//获取系统中哪些应用开启了Notification access
String flat = Settings.Secure.getStringForUser(
mContext.getContentResolver(),
Settings.Secure.ENABLED_NOTIFICATION_LISTENERS,
currentUser);
NotificationListenerInfo[] toRemove = new NotificationListenerInfo[mListeners.size()];
final ArrayList<ComponentName> toAdd;
synchronized (mNotificationList) {
// unbind and remove all existing listeners
toRemove = mListeners.toArray(toRemove);
toAdd = new ArrayList<ComponentName>();
final HashSet<ComponentName> newEnabled = new HashSet<ComponentName>();
final HashSet<String> newPackages = new HashSet<String>();
// decode the list of components
if (flat != null) {
String[] components = flat.split(ENABLED_NOTIFICATION_LISTENERS_SEPARATOR);
for (int i=0; i<components.length; i++) {
final ComponentName component
= ComponentName.unflattenFromString(components[i]);
if (component != null) {
newEnabled.add(component);
toAdd.add(component);
newPackages.add(component.getPackageName());
}
}
mEnabledListenersForCurrentUser = newEnabled;
mEnabledListenerPackageNames = newPackages;
}
}
//对全部NotificationListenerService所有unbindService操做
for (NotificationListenerInfo info : toRemove) {
final ComponentName component = info.component;
final int oldUser = info.userid;
Slog.v(TAG, "disabling notification listener for user " + oldUser + ": " + component);
unregisterListenerService(component, info.userid);
}
//对全部NotificationListenerService进行bindService操做
final int N = toAdd.size();
for (int i=0; i<N; i++) {
final ComponentName component = toAdd.get(i);
Slog.v(TAG, "enabling notification listener for user " + currentUser + ": "
+ component);
registerListenerService(component, currentUser);
}
}
在该方法中将获取系统中全部NotificationListenerService,并进行registerListenerService操做,代码以下:
[java] view plaincopy
private void registerListenerService(final ComponentName name, final int userid) {
//... ...省略
Intent intent = new Intent(NotificationListenerService.SERVICE_INTERFACE);
intent.setComponent(name);
intent.putExtra(Intent.EXTRA_CLIENT_LABEL,
R.string.notification_listener_binding_label);
intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity(
mContext, 0, new Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS), 0));
try {
if (DBG) Slog.v(TAG, "binding: " + intent);
//使用bindService启动NotificationListenerService
if (!mContext.bindServiceAsUser(intent,
new ServiceConnection() {
INotificationListener mListener;
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
synchronized (mNotificationList) {
mServicesBinding.remove(servicesBindingTag);
try {
mListener = INotificationListener.Stub.asInterface(service);
NotificationListenerInfo info = new NotificationListenerInfo(
mListener, name, userid, this);
service.linkToDeath(info, 0);
//service启动成功以后将相关信息添加到mListeners列表中,后续经过该列表触发回调
mListeners.add(info);
} catch (RemoteException e) {
// already dead
}
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
Slog.v(TAG, "notification listener connection lost: " + name);
}
},
Context.BIND_AUTO_CREATE,
new UserHandle(userid)))
//... ...省略
}
}
整个启动流程如图2所示:
图 2 NLS开机启动时序图
当系统安装或者卸载应用的时候,也会触发NotificationListenerService的启动。当一个使用NotificationListenerService的应用被卸载掉后,须要在Notification access界面清除相应的选项,或者当多用户切换时,也会更新NotificationListenerService的状态。在NotificationManagerService中监听了如下广播:
[java] view plaincopy
Intent.ACTION_PACKAGE_ADDED
Intent.ACTION_PACKAGE_REMOVED
Intent.ACTION_PACKAGE_RESTARTED
Intent.ACTION_PACKAGE_CHANGED
Intent.ACTION_QUERY_PACKAGE_RESTART
Intent.ACTION_USER_SWITCHED
这些广播在应用变动时由系统发出,好比安装、卸载、覆盖安装应用等等。当NotificationManagerService接收这些广播后编会调用rebindListenerServices,以后的流程就与前面同样。启动流程以下:
图 3 NLS广播启动
在NotificationManagerService中使用了ContentObserver监听SettingsProvider数据库变化,当Notification access有更新时,会更新NotificationListenerService的状态。例如,当用户进入Notification access界面,手动开启或关闭相关应用的Notification access权限时便会触发这种启动方式。当数据库中NotificationListenerService关联的信息改变后,会触发ContentObserver的onChange方法,继而调用update方法更新系统中NotificationListenerService的服务状态,最后调用到rebindListenerServices中。整个流程以下:
图 4 NLS数据库变动启动
在系统中实际上运行的是NotificationListenerService的子类,这些子类的启动方式分为三种:开机启动时NotificationManagerService初始化回调;接收相关广播后执行;数据库变动后执行。这些启动方式归根到底仍是bindService的操做。
前面提到了NotificationListenerService的启动流程,当启动完成以后就是调用,整个调用流程分为两种状况,即:新增通知和删除通知。
当系统收到新的通知消息时,会调用NotificationManager的notify方法用以发起系统通知,在notify方法中则调用关键方法enqueueNotificationWithTag:
[java] view plaincopy
service.enqueueNotificationWithTag(......)
这里的service是INotificationManager的对象,而NotificationManagerService继承自INotificationManager.Stub。也就是说NotificationManager与NotificationManagerService实际上就是client与server的关系,这里的service最终是NotificationManagerService的对象。这里便会跳转到NotificationManagerService的enqueueNotificationWithTag方法中,实际调用的是enqueueNotificationInternal方法。在该方法中就涉及到Notification的组装,以后调用关键方法notifyPostedLocked():
[java] view plaincopy
private void notifyPostedLocked(NotificationRecord n) {
final StatusBarNotification sbn = n.sbn.clone();
//这里触发mListeners中全部的NotificationListenerInfo回调
for (final NotificationListenerInfo info : mListeners) {
mHandler.post(new Runnable() {
@Override
public void run() {
info.notifyPostedIfUserMatch(sbn);
}});
}
}
到这里就开始准备回调了,由于前面通知已经组装完毕准备显示到状态栏了,以后就须要将相关的通知消息告诉全部监听者。继续看到notifyPostedIfUserMatch方法:
[java] view plaincopy
public void notifyPostedIfUserMatch(StatusBarNotification sbn) {
//... ...省略
try {
listener.onNotificationPosted(sbn);
} catch (RemoteException ex) {
Log.e(TAG, "unable to notify listener (posted): " + listener, ex);
}
}
上面的listener对象是NotificationListenerInfo类的全局变量,那是在哪里赋值的呢?还记得前面注册NotificationListenerService的时候bindServiceAsUser,其中new了一个ServiceConnection对象,并在其onServiceConnected方法中有以下代码:
[java] view plaincopy
public void onServiceConnected(ComponentName name, IBinder service) {
synchronized (mNotificationList) {
mServicesBinding.remove(servicesBindingTag);
try {
//mListener就是NotificationListenerService子类的对象
//service是INotificationListenerWrapper的对象,INotificationListenerWrapper
//继承自INotificationListener.Stub,是NotificationListenerService的内部类
mListener = INotificationListener.Stub.asInterface(service);
//使用mListener对象生成对应的NotificationListenerInfo对象
NotificationListenerInfo info = new NotificationListenerInfo(
mListener, name, userid, this);
service.linkToDeath(info, 0);
mListeners.add(info);
} catch (RemoteException e) {
// already dead
}
}
}
也就是说在NotificationListenerService启动并链接的时候,将binder对象保存到了NotificationListenerInfo中。这里就得看看NotificationListenerService的onBind方法返回了,代码以下:
[java] view plaincopy
@Override
public IBinder onBind(Intent intent) {
if (mWrapper == null) {
mWrapper = new INotificationListenerWrapper();
}
//这里返回的是INotificationListenerWrapper对象
return mWrapper;
}
private class INotificationListenerWrapper extends INotificationListener.Stub {
@Override
public void onNotificationPosted(StatusBarNotification sbn) {
try {
//onNotificationPosted是抽象方法之一
NotificationListenerService.this.onNotificationPosted(sbn);
} catch (Throwable t) {
Log.w(TAG, "Error running onNotificationPosted", t);
}
}
@Override
public void onNotificationRemoved(StatusBarNotification sbn) {
try {
//onNotificationRemoved是另外一个抽象方法
NotificationListenerService.this.onNotificationRemoved(sbn);
} catch (Throwable t) {
Log.w(TAG, "Error running onNotificationRemoved", t);
}
}
}
经过以上代码能够知道,当在notifyPostedIfUserMatch执行listener.onNotificationPosted方法时,实际上会调用到NotificationListenerService.INotificationListenerWrapper的onNotificationPosted方法。
NotificationListenerService是一个Abstract类,其中的Abstract方法是onNotificationPosted和onNotificationRemoved。当触发NotificationListenerService.INotificationListenerWrapper的onNotificationPosted方法时,继续调用了NotificationListenerService.this.onNotificationPosted(sbn)。这样会继续调用全部NotificationListenerService子类中的onNotificationPosted方法,系统通知新增的消息便传到了全部NotificationListenerService中。
从整个流程来看,新增通知的发起点是NotificationManager,处理通知则是由NotificationManagerService完成,传输过程是经过NotificationListenerService,最后回调方法是各个继承自NotificationListenerService的子类。整个过程的调用时序图以下:
图 5 onNotificationPosted触发流程
与"新增通知"相似的流程是"删除通知",发起点在NotificationManager,以后经由NotificationManagerService处理和NotificationListenerService传递,最后到达各个继承自NotificationListenerService的子类中,只不过最后的处理方法变成了onNotificationRemoved。调用时序图下:
图 6 onNotificationRemoved触发流程
简单来看,NotificationListenerService在系统通知的消息传递过程当中,起到了代理的做用。继承自NotificationListenerService的类做为client端,真正的server端则是NotificationManagerService,由它负责整个Notification的控制与管理。NotificationManagerService将处理以后的结果经过NotificationListenerService返回给client端,最终各个client端经过onNotificationPosted和onNotificationRemoved方法拿到系统通知状态变动的相关信息。
前文分析了整个NotificationListenerService的启动和调用,经过以上分析能够很清楚的了解NotificationListenerService的工做流程。在上一篇文章《Android 4.4 KitKat NotificationManagerService使用详解与原理分析(一)__使用详解》中,文末分析了在NotificationListenerService在使用过程当中的遇到的一些问题,但并无深究出现这些问题的根本缘由,下文会对这些问题进行详细分析。
当手机上没有安装任何使用NotificationListenerService的应用时,系统默认不会显示"Notification access"选项。只有手机中安装了使用NotificationListenerService的应用,才能够在"Settings > Security > Notification access" 找到对应的设置页面。在SourceCode/packages/apps/Settings/src/com/android/settings/SecuritySettings.java中能够看到以下初始化代码:
[java] view plaincopy
//... ...省略
mNotificationAccess = findPreference(KEY_NOTIFICATION_ACCESS);
if (mNotificationAccess != null) {
final int total = NotificationAccessSettings.getListenersCount(mPM);
if (total == 0) {
if (deviceAdminCategory != null) {
//若是系统中没有安装使用NLS的应用则删除显示
deviceAdminCategory.removePreference(mNotificationAccess);
}
} else {
//获取系统中有多少启动了Notification access的应用
final int n = getNumEnabledNotificationListeners();
//根据启用的数量显示不一样的Summary
if (n == 0) {
mNotificationAccess.setSummary(getResources().getString(
R.string.manage_notification_access_summary_zero));
} else {
mNotificationAccess.setSummary(String.format(getResources().getQuantityString(
R.plurals.manage_notification_access_summary_nonzero,
n, n)));
}
}
}
//... ...省略
有不少人在使用getActiveNotifications方法时返回为null,多数状况下是由于在onCreate或者在onBind中调用了getActiveNotifications方法。好比NotificaionMonitor extends NotificationListenerService:
[java] view plaincopy
public class NotificationMonitor extends NotificationListenerService {
@Override
public void onCreate() {
//getActiveNotifications();
super.onCreate();
}
@Override
public IBinder onBind(Intent intent) {
getActiveNotifications();
return super.onBind(intent);
}
}
找到NotificationListenerService中的getActiveNotifications方法实现,代码以下:
[java] view plaincopy
public StatusBarNotification[] getActiveNotifications() {
try {
//getActiveNotifications成功执行的两个关键点:
//1.getNotificationInterface方法返回正常
//2.mWrapper对象不为null
return getNotificationInterface().getActiveNotificationsFromListener(mWrapper);
} catch (android.os.RemoteException ex) {
Log.v(TAG, "Unable to contact notification manager", ex);
}
return null;
}
//经过查看能够知道,getNotificationInterface没有问题,若是为null会进行初始化
private final INotificationManager getNotificationInterface() {
if (mNoMan == null) {
mNoMan = INotificationManager.Stub.asInterface(
ServiceManager.getService(Context.NOTIFICATION_SERVICE));
}
return mNoMan;
}
//若是mWrapper为null则进行初始化
@Override
public IBinder onBind(Intent intent) {
if (mWrapper == null) {
mWrapper = new INotificationListenerWrapper();
}
return mWrapper;
}
经过上面的代码能够知道getActiveNotifications方法调用失败的缘由:
1. service的生命周期会先从onCraete->onBind逐步执行;
2. 此时调用getActiveNotifications方法会使用NotificationListenerService中的mWrapper对象;
3. mWrapper对象必须在NotificationMonitor完成super.onBind方法以后才会初始化;
综上所述,当在onCreate或者onBind方法中使用getActiveNotifications方法时,会致使mWrapper没有初始化,即mWrapper == null。解决方案能够在onCreate或者onBind方法中使用handler异步调用getActiveNotification方法,具体可参考《Android 4.4 KitKat NotificationManagerService使用详解与原理分析(一)__使用详解》。
若是NotificationMonitor在onCreate或onBind方法中出现crash,则该NotificationMonitor已经失效。就算修改了NotificationMonitor的代码不会再crash,但NotificationMonitor仍是不能收到onNotificationPosted和onNotificationRemoved回调,除非重启手机。
这个问题是google设计上的缺陷致使,出现NotificationListenerService失效的必要条件: 在NotificationMonitor的onCreate或者onBind中出现异常,致使service crash,也就是说service尚未彻底启动的状况下出现了异常致使退出。
这里须要回到NotificationManagerService中,NotificationListenerService的注册方法registerListenerService中:
[java] view plaincopy
private void registerListenerService(final ComponentName name, final int userid) {
//servicesBindingTag能够理解为须要启动的service的标签
final String servicesBindingTag = name.toString() + "/" + userid;
//若是mServicesBinding中已经包含正在处理的service则直接return退出
if (mServicesBinding.contains(servicesBindingTag)) {
// stop registering this thing already! we're working on it
return;
}
//将准备启动的service标签添加到mServicesBinding中
mServicesBinding.add(servicesBindingTag);
//... ...省略
//使用bindServiceAsUser启动service
Intent intent = new Intent(NotificationListenerService.SERVICE_INTERFACE);
intent.setComponent(name);
intent.putExtra(Intent.EXTRA_CLIENT_LABEL,
R.string.notification_listener_binding_label);
intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity(
mContext, 0, new Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS), 0));
try {
if (DBG) Slog.v(TAG, "binding: " + intent);
if (!mContext.bindServiceAsUser(intent,
new ServiceConnection() {
INotificationListener mListener;
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
synchronized (mNotificationList) {
//服务成功启动以后删除标签
mServicesBinding.remove(servicesBindingTag);
try {
mListener = INotificationListener.Stub.asInterface(service);
NotificationListenerInfo info = new NotificationListenerInfo(
mListener, name, userid, this);
service.linkToDeath(info, 0);
mListeners.add(info);
} catch (RemoteException e) {
// already dead
}
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
Slog.v(TAG, "notification listener connection lost: " + name);
}
},
Context.BIND_AUTO_CREATE,
new UserHandle(userid)))
{
//绑定服务失败后删除标签
mServicesBinding.remove(servicesBindingTag);
Slog.w(TAG, "Unable to bind listener service: " + intent);
return;
}
} catch (SecurityException ex) {
Slog.e(TAG, "Unable to bind listener service: " + intent, ex);
return;
}
}
}
当调用registerListenerService方法时,使用了一个mServicesBinding的ArrayList<String>用来记录当前正在启动的服务。在启动以前会判断当前service是否在mServicesBinding之中,若是是则代表正在执行bindServiceAsUser操做,直接退出,不然就继续执行bindServiceAsUser流程。调用bindServiceAsUser以前会在mServicesBinding中会添加标签,当链接成功以后也就是onServiceConnected返回后,以及绑定失败后会在mServicesBinding中删除标签。
google这样设计的目的多是为了不同一个service屡次启动,所以在执行bindServiceAsUser以前就打上标签,当处理完成以后(onServiceConnected回调)就删掉这个标签,代表这个service 绑定完成。可是,若是执行bindServiceAsUser以后,NotificationMonitor在onCreate或者onBind的时候crash了,也就是NotificationMonitor尚未完成启动,所以就不会去调用onServiceConnected方法,并最终致使不会调用 mServicesBinding.remove(servicesBindingTag)方法,从而使得NotificationMonitor的标签被一致记录在mServicesBinding中。那么当下一次想再次注册该服务的时候,系统发现该服务已经在mServicesBinding中了,因此直接return,后面的bindServiceAsUser就不会被调用了。
虽然代码已经更新,但service没法正常启动,那么onNotificationPosted和onNotificationRemoved的回调天然就没法使用,此时的解决办法就只能重启手机,清空mServicesBinding的值。
NotificationListenerService在系统通知获取的流程中,自身并无启动,而是起到了一个代理的做用,每个继承自NotificationListenerService的类,当系统通知变化后最终都会收到onNotificationPosted和onNotificationRemoved的回调。
bindService方法的返回与service是否成功启动无关,所以才会致使NotificationListenerService失效。
最后再看一下整个NotificationListenerService的关系类图:
图 7 NLS关系类图
示例代码:
https://github.com/yihongyuelan/NotificationListenerServiceDemo