NotificationManagerService使用詳解與原理分析(二)

前置文章:

《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啓動

        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在CODE上查看代碼片派生到個人代碼片

  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在CODE上查看代碼片派生到個人代碼片

  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在CODE上查看代碼片派生到個人代碼片

  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在CODE上查看代碼片派生到個人代碼片

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

這裏的service是INotificationManager的對象,而NotificationManagerService繼承自INotificationManager.Stub。也就是說NotificationManager與NotificationManagerService實際上就是client與server的關係,這裏的service最終是NotificationManagerService的對象。這裏便會跳轉到NotificationManagerService的enqueueNotificationWithTag方法中,實際調用的是enqueueNotificationInternal方法。在該方法中就涉及到Notification的組裝,以後調用關鍵方法notifyPostedLocked():

[java] view plaincopy在CODE上查看代碼片派生到個人代碼片

  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在CODE上查看代碼片派生到個人代碼片

  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在CODE上查看代碼片派生到個人代碼片

  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在CODE上查看代碼片派生到個人代碼片

  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.         try {  

  14.             //onNotificationPosted是抽象方法之一  

  15.             NotificationListenerService.this.onNotificationPosted(sbn);  

  16.         } catch (Throwable t) {  

  17.             Log.w(TAG, "Error running onNotificationPosted", t);  

  18.         }  

  19.     }  

  20.     @Override  

  21.     public void onNotificationRemoved(StatusBarNotification sbn) {  

  22.         try {  

  23.             //onNotificationRemoved是另外一個抽象方法  

  24.             NotificationListenerService.this.onNotificationRemoved(sbn);  

  25.         } catch (Throwable t) {  

  26.             Log.w(TAG, "Error running onNotificationRemoved", t);  

  27.         }  

  28.     }  

  29. }  

        經過以上代碼能夠知道,當在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在系統通知的消息傳遞過程當中,起到了代理的做用。繼承自NotificationListenerService的類做爲client端,真正的server端則是NotificationManagerService,由它負責整個Notification的控制與管理。NotificationManagerService將處理以後的結果經過NotificationListenerService返回給client端,最終各個client端經過onNotificationPosted和onNotificationRemoved方法拿到系統通知狀態變動的相關信息。

NotificationListenerService重點分析

        前文分析了整個NotificationListenerService的啓動和調用,經過以上分析能夠很清楚的瞭解NotificationListenerService的工做流程。在上一篇文章《Android 4.4 KitKat NotificationManagerService使用詳解與原理分析(一)__使用詳解》中,文末分析了在NotificationListenerService在使用過程當中的遇到的一些問題,但並無深究出現這些問題的根本緣由,下文會對這些問題進行詳細分析。

Notification access頁面不存在

        當手機上沒有安裝任何使用NotificationListenerService的應用時,系統默認不會顯示"Notification access"選項。只有手機中安裝了使用NotificationListenerService的應用,才能夠在"Settings > Security > Notification access" 找到對應的設置頁面。在SourceCode/packages/apps/Settings/src/com/android/settings/SecuritySettings.java中能夠看到以下初始化代碼:

[java] view plaincopy在CODE上查看代碼片派生到個人代碼片

  1. //... ...省略  

  2. mNotificationAccess = findPreference(KEY_NOTIFICATION_ACCESS);  

  3. if (mNotificationAccess != null) {  

  4.     final int total = NotificationAccessSettings.getListenersCount(mPM);  

  5.     if (total == 0) {  

  6.         if (deviceAdminCategory != null) {  

  7.             //若是系統中沒有安裝使用NLS的應用則刪除顯示  

  8.             deviceAdminCategory.removePreference(mNotificationAccess);  

  9.         }  

  10.     } else {  

  11.         //獲取系統中有多少啓動了Notification access的應用  

  12.         final int n = getNumEnabledNotificationListeners();  

  13.         //根據啓用的數量顯示不一樣的Summary  

  14.         if (n == 0) {  

  15.             mNotificationAccess.setSummary(getResources().getString(  

  16.                     R.string.manage_notification_access_summary_zero));  

  17.         } else {  

  18.             mNotificationAccess.setSummary(String.format(getResources().getQuantityString(  

  19.                     R.plurals.manage_notification_access_summary_nonzero,  

  20.                     n, n)));  

  21.         }  

  22.     }  

  23. }  

  24. //... ...省略  

getActiveNotifications()方法返回爲null

        有不少人在使用getActiveNotifications方法時返回爲null,多數狀況下是由於在onCreate或者在onBind中調用了getActiveNotifications方法。好比NotificaionMonitor extends NotificationListenerService:

[java] view plaincopy在CODE上查看代碼片派生到個人代碼片

  1. public class NotificationMonitor extends NotificationListenerService {  

  2. @Override      

  3. public void onCreate() {  

  4.     //getActiveNotifications();  

  5.     super.onCreate();  

  6. }  

  7.   

  8. @Override  

  9. public IBinder onBind(Intent intent) {  

  10.     getActiveNotifications();  

  11.     return super.onBind(intent);  

  12. }  

  13.   

  14. }  

找到NotificationListenerService中的getActiveNotifications方法實現,代碼以下:

[java] view plaincopy在CODE上查看代碼片派生到個人代碼片

  1. public StatusBarNotification[] getActiveNotifications() {  

  2.     try {  

  3.         //getActiveNotifications成功執行的兩個關鍵點:  

  4.         //1.getNotificationInterface方法返回正常  

  5.         //2.mWrapper對象不爲null  

  6.         return getNotificationInterface().getActiveNotificationsFromListener(mWrapper);  

  7.     } catch (android.os.RemoteException ex) {  

  8.         Log.v(TAG, "Unable to contact notification manager", ex);  

  9.     }  

  10.     return null;  

  11. }  

  12.   

  13. //經過查看能夠知道,getNotificationInterface沒有問題,若是爲null會進行初始化  

  14. private final INotificationManager getNotificationInterface() {  

  15.     if (mNoMan == null) {  

  16.         mNoMan = INotificationManager.Stub.asInterface(  

  17.                 ServiceManager.getService(Context.NOTIFICATION_SERVICE));  

  18.     }  

  19.     return mNoMan;  

  20. }  

  21.   

  22. //若是mWrapper爲null則進行初始化  

  23. @Override  

  24. public IBinder onBind(Intent intent) {  

  25.     if (mWrapper == null) {  

  26.         mWrapper = new INotificationListenerWrapper();  

  27.     }  

  28.     return mWrapper;  

  29. }  

經過上面的代碼能夠知道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使用詳解與原理分析(一)__使用詳解》

NotificationListenerService失效

        若是NotificationMonitor在onCreate或onBind方法中出現crash,則該NotificationMonitor已經失效。就算修改了NotificationMonitor的代碼不會再crash,但NotificationMonitor仍是不能收到onNotificationPosted和onNotificationRemoved回調,除非重啓手機。

        這個問題是google設計上的缺陷致使,出現NotificationListenerService失效的必要條件: 在NotificationMonitor的onCreate或者onBind中出現異常,致使service crash,也就是說service尚未徹底啓動的狀況下出現了異常致使退出。

        這裏須要回到NotificationManagerService中,NotificationListenerService的註冊方法registerListenerService中:

[java] view plaincopy在CODE上查看代碼片派生到個人代碼片

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

  2.     //servicesBindingTag能夠理解爲須要啓動的service的標籤  

  3.     final String servicesBindingTag = name.toString() + "/" + userid;  

  4.     //若是mServicesBinding中已經包含正在處理的service則直接return退出  

  5.     if (mServicesBinding.contains(servicesBindingTag)) {  

  6.     // stop registering this thing already! we're working on it  

  7.        return;  

  8.     }  

  9.     //將準備啓動的service標籤添加到mServicesBinding中  

  10.     mServicesBinding.add(servicesBindingTag);  

  11.   

  12.     //... ...省略  

  13.     //使用bindServiceAsUser啓動service  

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

  15.     intent.setComponent(name);  

  16.   

  17.     intent.putExtra(Intent.EXTRA_CLIENT_LABEL,  

  18.             R.string.notification_listener_binding_label);  

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

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

  21.   

  22.     try {  

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

  24.         if (!mContext.bindServiceAsUser(intent,  

  25.                 new ServiceConnection() {  

  26.                     INotificationListener mListener;  

  27.                     @Override  

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

  29.                         synchronized (mNotificationList) {  

  30.                             //服務成功啓動以後刪除標籤  

  31.                             mServicesBinding.remove(servicesBindingTag);  

  32.                             try {  

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

  34.                                 NotificationListenerInfo info = new NotificationListenerInfo(  

  35.                                         mListener, name, userid, this);  

  36.                                 service.linkToDeath(info, 0);  

  37.                                 mListeners.add(info);  

  38.                             } catch (RemoteException e) {  

  39.                                 // already dead  

  40.                             }  

  41.                         }  

  42.                     }  

  43.   

  44.                     @Override  

  45.                     public void onServiceDisconnected(ComponentName name) {  

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

  47.                     }  

  48.                 },  

  49.                 Context.BIND_AUTO_CREATE,  

  50.                 new UserHandle(userid)))  

  51.         {  

  52.             //綁定服務失敗後刪除標籤  

  53.             mServicesBinding.remove(servicesBindingTag);  

  54.             Slog.w(TAG, "Unable to bind listener service: " + intent);  

  55.             return;  

  56.         }  

  57.     } catch (SecurityException ex) {  

  58.         Slog.e(TAG, "Unable to bind listener service: " + intent, ex);  

  59.         return;  

  60.     }  

  61. }  

  62. }  

        當調用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

相關文章
相關標籤/搜索