欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

NotificationManagerService 的使用细节和原理分析(二)

最编程 2024-05-07 16:59:16
...
开源中国社区团队直播首秀,以分享为名讲述开源中国社区背后的故事”

前置文章:

《Android 4.4 KitKat NotificationManagerService使用详解与原理分析(一)__使用详解》

转载请务必注明出处:http://blog.****.net/yihongyuelan

概况

        在上一篇文章《Android 4.4 KitKat NotificationManagerService使用详解与原理分析(一)__使用详解》中详细介绍了NotificationListenerService的使用方法,以及在使用过程中遇到的问题和规避方案。本文主要分析NotificationListenerService实现原理,以及详细分析在上一篇文章中提到的相关问题和产生的根本原因。在原理分析前,先看看NotificationListenerService涉及到的类以及基本作用,如图1所示:


图 1 NLS注册及回调过程

通过图1可以看到,整个通知状态获取分为三部分:

①. 监听器注册;新建一个类NotificationMonitor继承自NotificationListenerService。

②. 系统通知管理;系统通知管理由NotificationManagerService负责。

③. 通知状态回调;当系统通知状态改变之后,NotificationManagerService会通知NotificationListenerService,最后再由NotificationListenerService通知其所有子类。

在整个系统中,通知管理是由NotificationManagerService完成的,NotificationListenerService只是在通知改变时,会获得相应的通知消息,这些消息最终会回调到NotificationListenerService的所有子类中。

NotificationListenerService启动

        NotificationListenerService虽然继承自Service,但系统中实际上启动的是其子类,为了表述方便,后文统一使用NotificationListenerService启动来指代。其子类的启动有三个途径,分别是:开机启动、接收PACKAGE相关广播(安装、卸载等)启动、SettingsProvider数据变更启动。

        既然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

  1. void rebindListenerServices() {  

  2.     final int currentUser = ActivityManager.getCurrentUser();  

  3.     //获取系统中哪些应用开启了Notification access  

  4.     String flat = Settings.Secure.getStringForUser(  

  5.             mContext.getContentResolver(),  

  6.             Settings.Secure.ENABLED_NOTIFICATION_LISTENERS,  

  7.             currentUser);  

  8.   

  9.     NotificationListenerInfo[] toRemove = new NotificationListenerInfo[mListeners.size()];  

  10.     final ArrayList<ComponentName> toAdd;  

  11.   

  12.     synchronized (mNotificationList) {  

  13.         // unbind and remove all existing listeners  

  14.         toRemove = mListeners.toArray(toRemove);  

  15.   

  16.         toAdd = new ArrayList<ComponentName>();  

  17.         final HashSet<ComponentName> newEnabled = new HashSet<ComponentName>();  

  18.         final HashSet<String> newPackages = new HashSet<String>();  

  19.   

  20.         // decode the list of components  

  21.         if (flat != null) {  

  22.             String[] components = flat.split(ENABLED_NOTIFICATION_LISTENERS_SEPARATOR);  

  23.             for (int i=0; i<components.length; i++) {  

  24.                 final ComponentName component  

  25.                         = ComponentName.unflattenFromString(components[i]);  

  26.                 if (component != null) {  

  27.                     newEnabled.add(component);  

  28.                     toAdd.add(component);  

  29.                     newPackages.add(component.getPackageName());  

  30.                 }  

  31.             }  

  32.   

  33.             mEnabledListenersForCurrentUser = newEnabled;  

  34.             mEnabledListenerPackageNames = newPackages;  

  35.         }  

  36.     }  

  37.   

  38.     //对所有NotificationListenerService全部unbindService操作  

  39.     for (NotificationListenerInfo info : toRemove) {  

  40.         final ComponentName component = info.component;  

  41.         final int oldUser = info.userid;  

  42.         Slog.v(TAG, "disabling notification listener for user " + oldUser + ": " + component);  

  43.         unregisterListenerService(component, info.userid);  

  44.     }  

  45.     //对所有NotificationListenerService进行bindService操作  

  46.     final int N = toAdd.size();  

  47.     for (int i=0; i<N; i++) {  

  48.         final ComponentName component = toAdd.get(i);  

  49.         Slog.v(TAG, "enabling notification listener for user " + currentUser + ": "  

  50.                 + component);  

  51.         registerListenerService(component, currentUser);  

  52.     }  

  53. }  

在该方法中将获取系统中所有NotificationListenerService,并进行registerListenerService操作,代码如下:

[java] view plaincopy

  1. private void registerListenerService(final ComponentName name, final int userid) {  

  2. //... ...省略  

  3.   

  4. Intent intent = new Intent(NotificationListenerService.SERVICE_INTERFACE);  

  5. intent.setComponent(name);  

  6.   

  7. intent.putExtra(Intent.EXTRA_CLIENT_LABEL,  

  8.         R.string.notification_listener_binding_label);  

  9. intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity(  

  10.         mContext, 0new Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS), 0));  

  11.   

  12. try {  

  13.     if (DBG) Slog.v(TAG, "binding: " + intent);  

  14.     //使用bindService启动NotificationListenerService  

  15.     if (!mContext.bindServiceAsUser(intent,  

  16.             new ServiceConnection() {  

  17.                 INotificationListener mListener;  

  18.                 @Override  

  19.                 public void onServiceConnected(ComponentName name, IBinder service) {  

  20.                     synchronized (mNotificationList) {  

  21.                         mServicesBinding.remove(servicesBindingTag);  

  22.                         try {  

  23.                             mListener = INotificationListener.Stub.asInterface(service);  

  24.                             NotificationListenerInfo info = new NotificationListenerInfo(  

  25.                                     mListener, name, userid, this);  

  26.                             service.linkToDeath(info, 0);  

  27.                             //service启动成功之后将相关信息添加到mListeners列表中,后续通过该列表触发回调  

  28.                             mListeners.add(info);  

  29.                         } catch (RemoteException e) {  

  30.                             // already dead  

  31.                         }  

  32.                     }  

  33.                 }  

  34.   

  35.                 @Override  

  36.                 public void onServiceDisconnected(ComponentName name) {  

  37.                     Slog.v(TAG, "notification listener connection lost: " + name);  

  38.                 }  

  39.             },  

  40.             Context.BIND_AUTO_CREATE,  

  41.             new UserHandle(userid)))  

  42.     //... ...省略  

  43.     }  

  44. }  

整个启动流程如图2所示:


图 2 NLS开机启动时序图

广播启动

        当系统安装或者卸载应用的时候,也会触发NotificationListenerService的启动。当一个使用NotificationListenerService的应用被卸载掉后,需要在Notification access界面清除相应的选项,或者当多用户切换时,也会更新NotificationListenerService的状态。在NotificationManagerService中监听了以下广播:

[java] view plaincopy

  1. Intent.ACTION_PACKAGE_ADDED  

  2. Intent.ACTION_PACKAGE_REMOVED  

  3. Intent.ACTION_PACKAGE_RESTARTED  

  4. Intent.ACTION_PACKAGE_CHANGED  

  5. Intent.ACTION_QUERY_PACKAGE_RESTART  

  6. 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启动小结

        在系统中实际上运行的是NotificationListenerService的子类,这些子类的启动方式分为三种:开机启动时NotificationManagerService初始化回调;接收相关广播后执行;数据库变更后执行。这些启动方式归根到底还是bindService的操作。

NotificationListenerService调用流程

前面提到了NotificationListenerService的启动流程,当启动完成之后就是调用,整个调用流程分为两种情况,即:新增通知和删除通知。

新增通知

当系统收到新的通知消息时,会调用NotificationManager的notify方法用以发起系统通知,在notify方法中则调用关键方法enqueueNotificationWithTag:

[java] view plaincopy

  1. service.enqueueNotificationWithTag(......)  

这里的service是INotificationManager的对象,而NotificationManagerService继承自INotificationManager.Stub。也就是说NotificationManager与NotificationManagerService实际上就是client与server的关系,这里的service最终是NotificationManagerService的对象。这里便会跳转到NotificationManagerService的enqueueNotificationWithTag方法中,实际调用的是enqueueNotificationInternal方法。在该方法中就涉及到Notification的组装,之后调用关键方法notifyPostedLocked():

[java] view plaincopy

  1. private void notifyPostedLocked(NotificationRecord n) {  

  2.     final StatusBarNotification sbn = n.sbn.clone();  

  3.     //这里触发mListeners中所有的NotificationListenerInfo回调  

  4.     for (final NotificationListenerInfo info : mListeners) {  

  5.         mHandler.post(new Runnable() {  

  6.             @Override  

  7.             public void run() {  

  8.                 info.notifyPostedIfUserMatch(sbn);  

  9.             }});  

  10.     }  

  11. }  

到这里就开始准备回调了,因为前面通知已经组装完毕准备显示到状态栏了,之后就需要将相关的通知消息告诉所有监听者。继续看到notifyPostedIfUserMatch方法:

[java] view plaincopy

  1. public void notifyPostedIfUserMatch(StatusBarNotification sbn) {  

  2.     //... ...省略  

  3.     try {  

  4.         listener.onNotificationPosted(sbn);  

  5.     } catch (RemoteException ex) {  

  6.         Log.e(TAG, "unable to notify listener (posted): " + listener, ex);  

  7.     }  

  8. }  

上面的listener对象是NotificationListenerInfo类的全局变量,那是在哪里赋值的呢?还记得前面注册NotificationListenerService的时候bindServiceAsUser,其中new了一个ServiceConnection对象,并在其onServiceConnected方法中有如下代码:

[java] view plaincopy

  1. public void onServiceConnected(ComponentName name, IBinder service) {  

  2. synchronized (mNotificationList) {  

  3.     mServicesBinding.remove(servicesBindingTag);  

  4.     try {  

  5.         //mListener就是NotificationListenerService子类的对象  

  6.         //service是INotificationListenerWrapper的对象,INotificationListenerWrapper  

  7.         //继承自INotificationListener.Stub,是NotificationListenerService的内部类  

  8.         mListener = INotificationListener.Stub.asInterface(service);  

  9.         //使用mListener对象生成对应的NotificationListenerInfo对象  

  10.         NotificationListenerInfo info = new NotificationListenerInfo(  

  11.                 mListener, name, userid, this);  

  12.         service.linkToDeath(info, 0);  

  13.         mListeners.add(info);  

  14.     } catch (RemoteException e) {  

  15.         // already dead  

  16.     }  

  17. }  

  18. }  

也就是说在NotificationListenerService启动并连接的时候,将binder对象保存到了NotificationListenerInfo中。这里就得看看NotificationListenerService的onBind方法返回了,代码如下:

[java] view plaincopy

  1. @Override  

  2. public IBinder onBind(Intent intent) {  

  3.     if (mWrapper == null) {  

  4.         mWrapper = new INotificationListenerWrapper();  

  5.     }  

  6.     //这里返回的是INotificationListenerWrapper对象  

  7.     return mWrapper;  

  8. }  

  9.   

  10. private class INotificationListenerWrapper extends INotificationListener.Stub {  

  11.     @Override  

  12.     public void onNotificationPosted(StatusBarNotification sbn) {  

  13.         

    上一篇: 在苹果 8plus 中删除过期短信的步骤

    下一篇: 如何快速删除短信 - 掘金

推荐阅读