NotificationListenerService不能監聽到通知

做者:Hugo
連接:https://www.zhihu.com/question/33540416/answer/113706620
來源:知乎
著做權歸做者全部,轉載請聯繫做者得到受權。

背景知識:

所屬:android.service.notification.NotificationListenerServicejava

做用:監聽通知欄內容變化的服務android

  1. extends Service,abstract class(意味着第三方能夠實現去接收通知欄的通知數據)。
  2. Added in API level 18(Android 4.3)。
  3. 應用場景:智能手錶(Google官方的Android Wear手機端App,通知消息同步到手錶。以下圖)、紅包助手(監聽通知欄的微信紅包消息)等。
  4. Service bind時機:在系統的設置通知受權中勾選並受權時。
  5. 回調時機:有新通知或通知被移除或通知排序變化時系統回調。

----坑------

應用進程被殺後再次啓動時,服務不生效(沒有bindService)(在下圖所示的藍色列表名單中,不在紅色的存活名單中)。

影響:通知欄有內容變動,服務沒法感知。shell

還原方法:重啓手機微信

必現手機(方便調試):小米Note Pro,清除後臺應用後。app

咱們要作的:讓服務重生。源碼分析

調試手段:查看存活的通知監聽服務。ui

方法:adb shell dumpsys notificationthis


藍色:已受權的通知監聽Service列表。spa

紅色:當前存活的的通知監聽Service列表。調試


調查思路:
1、第三方應用主動註冊
2、觸發系統從新bind

思路一:第三方應用主動註冊

關鍵代碼路徑:

  • android.service.notification.NotificationListenerService#registerAsSystemService
  • android.app.INotificationManager.Stub#enforceSystemOrSystemUI

條件:

  • 系統的uid或有android.permission.STATUS_BAR_SERVICE權限。

∴ 路不通。


思路二:觸發系統從新bind

關鍵代碼路徑:

  • com.android.server.notification.ManagedServices#rebindServices

三種方式觸發:

  1. A && B(A:應用安裝卸載或更新等的廣播;B:上圖藍色列表中的服務有變化)。
  2. 系統的登陸用戶切換 。[pass]
  3. Settings.Secure.ENABLED_NOTIFICATION_LISTENERS的Settings值有變動。

第三方有權利觸發的方式(源碼分析得知 1B= 3):

  • Service的disable,會有Intent.ACTION_PACKAGE_CHANGED廣播,而且從上圖藍色列表中移除。

利用這一特性,把應用的NotificationListenerService實現類disable再enable,便可觸發系統rebind操做。


private void toggleNotificationListenerService() { PackageManager pm = getPackageManager(); pm.setComponentEnabledSetting(new ComponentName(this, com.xinghui.notificationlistenerservicedemo.NotificationListenerServiceImpl.class), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP); pm.setComponentEnabledSetting(new ComponentName(this, com.xinghui.notificationlistenerservicedemo.NotificationListenerServiceImpl.class), PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP); } 

問題解決。


補充:

一、怎樣在代碼中判斷本身的服務是否在上圖藍色列表(通知已受權)中?


private static boolean isNotificationListenerServiceEnabled(Context context) {
        Set<String> packageNames = NotificationManagerCompat.getEnabledListenerPackages(context);
        if (packageNames.contains(context.getPackageName())) {
            return true;
        }
        return false;
    }

二、怎樣發起通知受權流程。


startActivity(new Intent(NotificationConstants.ACTION_NOTIFICATION_LISTENER_SETTINGS));
相關文章
相關標籤/搜索