NotificationManagerService使用详解与原理分析(一)

2015-05-12  来源:本站原创  分类:Android  人气:283 

概况

Android在4.3的版本中(即API 18)加入了NotificationListenerService,根据SDK的描述(AndroidDeveloper)可以知道,当系统收到新的通知或者通知被删除时,会触发NotificationListenerService的回调方法。同时在Android 4.4 中新增了Notification.extras 字段,也就是说可以使用NotificationListenerService获取系统通知具体信息,这在以前是需要用反射来实现的。

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

重要关系

对于系统通知,三方APP使用NotificationListenerService主要目的是为了获取系统通知相关信息,主要包括:通知的新增和删除,获取当前通知数量,通知内容相关信息等。这些信息可以通过NotificationListenerService类提供的方法以及StatusBarNotification类对象来获取。

NotificationListenerService主要方法(成员变量):

cancelAllNotifications() :删除系统中所有可被清除的通知;
cancelNotification(String pkg, String tag, int id) :删除具体某一个通知;
getActiveNotifications() :返回当前系统所有通知到StatusBarNotification[];
onNotificationPosted(StatusBarNotification sbn) :当系统收到新的通知后出发回调;
onNotificationRemoved(StatusBarNotification sbn) :当系统通知被删掉后出发回调;

以上是NotificationListenerService的主要方法,通过这些方法就可以在应用中操作系统通知,在NotificationListenerService中除了对通知的操作之外,还可以获取到通知的StatusBarNotification对象,通过该对象可以获取通知更详细的数据。

StatusBarNotification主要方法(成员变量):

getId():返回通知对应的id;
getNotification():返回通知对象;
getPackageName():返回通知对应的包名;
getPostTime():返回通知发起的时间;
getTag():返回通知的Tag,如果没有设置返回null;
getUserId():返回UserId,用于多用户场景;
isClearable():返回该通知是否可被清楚,FLAG_ONGOING_EVENT、FLAG_NO_CLEAR;
isOngoing():检查该通知的flag是否为FLAG_ONGOING_EVENT;

使用简介

正确使用NotificationListenerService需要注意三点:

(1). 新建一个类并继承自NotificationListenerService,override其中重要的两个方法;

[java] view plaincopy

  1. public class NotificationMonitor extends NotificationListenerService {
  2. @Override
  3. public void onNotificationPosted(StatusBarNotification sbn) {
  4. Log.i("SevenNLS","Notification posted");
  5. }
  6. @Override
  7. public void onNotificationRemoved(StatusBarNotification sbn) {
  8. Log.i("SevenNLS","Notification removed");
  9. }
  10. }

(2). 在AndroidManifest.xml中注册Service并声明相关权限;

[html] view plaincopy

  1. <service android:name=".NotificationMonitor"
  2. android:label="@string/service_name"
  3. android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
  4. <intent-filter>
  5. <action android:name="android.service.notification.NotificationListenerService" />
  6. </intent-filter>
  7. </service>

(3). 开启NotificationMonitor的监听功能;

完成以上两步之后,将程序编译并安装到手机上,但此时该程序是无法监听到新增通知和删除通知的,还需要在"Settings > Security > Notification access"中,勾选NotificationMonitor。此时如果系统收到新的通知或者通知被删除就会打印出相应的log了。

这里需要注意,如果手机上没有安装使用NotificationListenerService类的APP,Notification access是不会显示出来的。可以在源码/packages/apps/Settings/src/com/android/settings/SecuritySettings.java中看到,如果没有使用NotificationListenerService的APK,直接就不显示这一项了。

[java] view plaincopy

  1. mNotificationAccess = findPreference(KEY_NOTIFICATION_ACCESS);
  2. if (mNotificationAccess != null) {
  3. final int total = NotificationAccessSettings.getListenersCount(mPM);
  4. if (total == 0) {
  5. if (deviceAdminCategory != null) {
  6. deviceAdminCategory.removePreference(mNotificationAccess);
  7. }
  8. } else {
  9. final int n = getNumEnabledNotificationListeners();
  10. if (n == 0) {
  11. mNotificationAccess.setSummary(getResources().getString(
  12. R.string.manage_notification_access_summary_zero));
  13. } else {
  14. mNotificationAccess.setSummary(String.format(getResources().getQuantityString(
  15. R.plurals.manage_notification_access_summary_nonzero,
  16. n, n)));
  17. }
  18. }
  19. }

使用详解

通过前面的讲解(实际上就是对AndroidDeveloper的解释),已经可以正常使用NotificationListenerService了,但对于实际应用中,需要考虑的事情还比较多。比如:

1. 如何检测应用已开启Notification access监听功能?

如果检测到应用没有激活Notification access监听功能,需要提示用户开启;

2. 能不能主动跳转到Notification access监听页面?

如果能够根据第1步的判断自动跳转到对应的页面,那可以省掉很多操作;

3. 如何与NotificationListenerService交互?

涉及到与Service的交互,但又与普通的Service不同,这里后文解释;

4. NotificationListenerService使用过程中有哪些注意事项?

在使用NotificationListenerService过程中自己遇到了一些坑,后文会通过分析给出相应的解决方案;

程序运行截图

NotificationManagerService使用详解与原理分析(一)
NotificationManagerService使用详解与原理分析(一)

图 1 程序运行截图

示例介绍

NotificationListenerDemo主要用于获取系统当前通知信息,并可手动创建"可清除通知",逐条删除"可清除通知",一次性删除"可清除通知",以及显示系统当前活动的通知信息。实际上该示例回答了前面使用详解中提出的各项疑问,在实际使用过程中相信大部分人都会遇到,因此这里逐条展开与大家分享。

NotificationManagerService使用详解与原理分析(一)

图 2 主界面

功能分析

1. 如何检测应用已开启Notification access监听功能?

在程序启动时,执行Notification access的检测,查看是否访问Notification的权限。如果用户没有Enable Notification access,则弹出提示对话框,点击OK跳转到Notification access设置页面。

NotificationManagerService使用详解与原理分析(一)
NotificationManagerService使用详解与原理分析(一)

图 3 首次启动 isEnable

使用NotificationListenerService的应用如果开启了Notification access,系统会将包名等相关信息写入SettingsProver数据库中,因此可以从数据库中获取相关信息并过滤,从而判断应用是否开启了Notification access,代码如下:

[java] view plaincopy

  1. private static final String ENABLED_NOTIFICATION_LISTENERS = "enabled_notification_listeners";
  2. private boolean isEnabled() {
  3. String pkgName = getPackageName();
  4. final String flat = Settings.Secure.getString(getContentResolver(),
  5. ENABLED_NOTIFICATION_LISTENERS);
  6. if (!TextUtils.isEmpty(flat)) {
  7. final String[] names = flat.split(":");
  8. for (int i = 0; i < names.length; i++) {
  9. final ComponentName cn = ComponentName.unflattenFromString(names[i]);
  10. if (cn != null) {
  11. if (TextUtils.equals(pkgName, cn.getPackageName())) {
  12. return true;
  13. }
  14. }
  15. }
  16. }
  17. return false;
  18. }

在返回值flat中如果包含了应用的包名,即可确定应用已开启Notification access,反之则表示没有开启。

2. 能不能主动跳转到Notification access监听页面?

通过查看可以知道,Notification access界面接收action为"android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS"的intent启动,因此使用startActivity可以很容易的跳转到该页面,从而避免用户在Settings中查找。代码如下:

[java] view plaincopy

  1. private static final String ACTION_NOTIFICATION_LISTENER_SETTINGS = "android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS";
  2. private void openNotificationAccess() {
  3. startActivity(new Intent(ACTION_NOTIFICATION_LISTENER_SETTINGS));
  4. }

3. 如何与NotificationListenerService交互?

因为NotificationListenerService中包含了四个重要的方法,分别是:onNotificationPosted、onNotificationRemoved、cancelNotification、cancelAllNotifications。通过这些方法我们才能实现诸如通知信息的获取以及删除等功能,虽然这些方法是public的,那是不是意味着我们只要拿到NotificationListenerService的对象就可以直接调用这些方法了呢?那如何拿到Service的对象呢?在之前的博文中,曾有提到与Service的交互( 具体可参考拙作《Android中程序与Service交互的方式——交互方式》),可以看到与Service的交互有很多种方法,但如果要拿到Service的对象,归根到底还是需要Binder。

也就是说得使用bindService的办法,将onServiceConnected回调中的IBinder对象转型成NotificationListenerService的对象。测试代码如下:

[java] view plaincopy

  1. //在MainActivity.java的onCreate方法中使用bindService帮顶NotificationMonitor服务
  2. bindService(new Intent(this,NotificationMonitor.class ), new ServiceConnection() {
  3. @Override
  4. public void onServiceDisconnected(ComponentName arg0) {
  5. }
  6. @Override
  7. public void onServiceConnected(ComponentName arg0, IBinder arg1) {
  8. NotificationMonitor.MyBinder localBinder = (MyBinder)arg1;
  9. NotificationMonitor mMonitor = localBinder.getService();
  10. }
  11. }, BIND_AUTO_CREATE);

[java] view plaincopy

  1. //NotificationMonitor的onBind方法返回构造的Binder对象
  2. public class NotificationMonitor extends NotificationListenerService {
  3. private MyBinder mBinder = new MyBinder();
  4. public class MyBinder extends Binder{
  5. public NotificationMonitor getService(){
  6. return NotificationMonitor.this;
  7. }
  8. }
  9. @Override
  10. public IBinder onBind(Intent arg0) {
  11. return mBinder;
  12. }
  13. @Override
  14. public void onNotificationPosted(StatusBarNotification sbn) {
  15. getActiveNotifications();
  16. cancelAllNotifications();
  17. }
  18. @Override
  19. public void onNotificationRemoved(StatusBarNotification sbn) {
  20. }
  21. }

那这样操作之后是不是就意味着可以拿到NotificationMonitor的对象并直接调用getActiveNotifications()方法,用于获取当前系统通知的信息了呢?很抱歉,事实证明这样是不行的。这里简单的分析下,在后面的NotificationListenerService原理分析中再详细讲解。在NotificationListenerService的源码中可以看到:

[java] view plaincopy

  1. @Override
  2. public IBinder onBind(Intent intent) {
  3. if (mWrapper == null) {
  4. mWrapper = new INotificationListenerWrapper();
  5. }
  6. return mWrapper;
  7. }

这里的INotificationListenerWrapper是NotificationListenerService的一个内部类:

[java] view plaincopy

  1. private class INotificationListenerWrapper extends INotificationListener.Stub

而NotificationMonitor继承自NotificationListenerService,默认的onBind方法却是:

[java] view plaincopy

  1. @Override
  2. public IBinder onBind(Intent intent) {
  3. return super.onBind(intent);
  4. }

这里注意,一般情况下service的onBind方法返回要么是null要么是Binder对象,可这里直接调用父类NotificationListenerService的onBind方法,而父类返回的是INotificationListenerWrapper的对象。这说明Binder对象已经被指定了,不能再给NotificationMonitor指定其它的Binder对象。如果你非要给NotificationMonitor指定其它的Binder对象,那么就无法使用INotificationListenerWrapper提供的方法。也就是说要么就用系统NotificationListenerService提供的方法,要么就把NotificationMonitor当一个普通的Service来用,系统提供的方法都不能使用。

那应该如何使用NotificationListenerService中的方法呢?在拙作《Android中程序与Service交互的方式——交互方式》中,已经提供了很多的例子,这里仅以广播的方式为例。

既然NotificationMonitor可以使用NotificationListenerService的方法,那通过NotificationMonitor把通知状态的改变以及数据获取到,并使用static数据进行存储,之后再在MainActivity中直接使用即可。在MainActivity中控制通知的单个删除和全部删除,则使用广播的方式发送给NotificationMonitor进行处理。MainActivity与NotificationMonitor的关系类图如下:

NotificationManagerService使用详解与原理分析(一)

图 4 结构类图

NotificationMonitor和MainActivity关键代码如下:

[java] view plaincopy

  1. public class NotificationMonitor extends NotificationListenerService {
  2. private static final String TAG = "SevenNLS";
  3. private static final String TAG_PRE = "[" + NotificationMonitor.class.getSimpleName() + "] ";
  4. private static final int EVENT_UPDATE_CURRENT_NOS = 0;
  5. public static final String ACTION_NLS_CONTROL = "com.seven.notificationlistenerdemo.NLSCONTROL";
  6. //用于存储当前所有的Notification的StatusBarNotification对象数组
  7. public static List<StatusBarNotification[]> mCurrentNotifications = new ArrayList<StatusBarNotification[]>();
  8. public static int mCurrentNotificationsCounts = 0;
  9. //收到新通知后将通知的StatusBarNotification对象赋值给mPostedNotification
  10. public static StatusBarNotification mPostedNotification;
  11. //删除一个通知后将通知的StatusBarNotification对象赋值给mRemovedNotification
  12. public static StatusBarNotification mRemovedNotification;
  13. private CancelNotificationReceiver mReceiver = new CancelNotificationReceiver();
  14. // String a;
  15. private Handler mMonitorHandler = new Handler() {
  16. @Override
  17. public void handleMessage(Message msg) {
  18. switch (msg.what) {
  19. case EVENT_UPDATE_CURRENT_NOS:
  20. updateCurrentNotifications();
  21. break;
  22. default:
  23. break;
  24. }
  25. }
  26. };
  27. class CancelNotificationReceiver extends BroadcastReceiver {
  28. @Override
  29. public void onReceive(Context context, Intent intent) {
  30. String action;
  31. if (intent != null && intent.getAction() != null) {
  32. action = intent.getAction();
  33. if (action.equals(ACTION_NLS_CONTROL)) {
  34. String command = intent.getStringExtra("command");
  35. if (TextUtils.equals(command, "cancel_last")) {
  36. if (mCurrentNotifications != null && mCurrentNotificationsCounts >= 1) {
  37. //每次删除通知最后一个
  38. StatusBarNotification sbnn = getCurrentNotifications()[mCurrentNotificationsCounts - 1];
  39. cancelNotification(sbnn.getPackageName(), sbnn.getTag(), sbnn.getId());
  40. }
  41. } else if (TextUtils.equals(command, "cancel_all")) {
  42. //删除所有通知
  43. cancelAllNotifications();
  44. }
  45. }
  46. }
  47. }
  48. }
  49. @Override
  50. public void onCreate() {
  51. super.onCreate();
  52. logNLS("onCreate...");
  53. IntentFilter filter = new IntentFilter();
  54. filter.addAction(ACTION_NLS_CONTROL);
  55. registerReceiver(mReceiver, filter);
  56. //在onCreate时第一次调用getActiveNotifications()
  57. mMonitorHandler.sendMessage(mMonitorHandler.obtainMessage(EVENT_UPDATE_CURRENT_NOS));
  58. }
  59. @Override
  60. public void onDestroy() {
  61. super.onDestroy();
  62. unregisterReceiver(mReceiver);
  63. }
  64. @Override
  65. public IBinder onBind(Intent intent) {
  66. // a.equals("b");
  67. logNLS("onBind...");
  68. return super.onBind(intent);
  69. }
  70. @Override
  71. public void onNotificationPosted(StatusBarNotification sbn) {
  72. //当系统收到新的通知后,更新mCurrentNotifications列表
  73. updateCurrentNotifications();
  74. logNLS("onNotificationPosted...");
  75. logNLS("have " + mCurrentNotificationsCounts + " active notifications");
  76. mPostedNotification = sbn;
  77. //通过以下方式可以获取Notification的详细信息
  78. /*
  79. * Bundle extras = sbn.getNotification().extras; String
  80. * notificationTitle = extras.getString(Notification.EXTRA_TITLE);
  81. * Bitmap notificationLargeIcon = ((Bitmap)
  82. * extras.getParcelable(Notification.EXTRA_LARGE_ICON)); Bitmap
  83. * notificationSmallIcon = ((Bitmap)
  84. * extras.getParcelable(Notification.EXTRA_SMALL_ICON)); CharSequence
  85. * notificationText = extras.getCharSequence(Notification.EXTRA_TEXT);
  86. * CharSequence notificationSubText =
  87. * extras.getCharSequence(Notification.EXTRA_SUB_TEXT);
  88. * Log.i("SevenNLS", "notificationTitle:"+notificationTitle);
  89. * Log.i("SevenNLS", "notificationText:"+notificationText);
  90. * Log.i("SevenNLS", "notificationSubText:"+notificationSubText);
  91. * Log.i("SevenNLS",
  92. * "notificationLargeIcon is null:"+(notificationLargeIcon == null));
  93. * Log.i("SevenNLS",
  94. * "notificationSmallIcon is null:"+(notificationSmallIcon == null));
  95. */
  96. }
  97. @Override
  98. public void onNotificationRemoved(StatusBarNotification sbn) {
  99. //当有通知被删除后,更新mCurrentNotifications列表
  100. updateCurrentNotifications();
  101. logNLS("removed...");
  102. logNLS("have " + mCurrentNotificationsCounts + " active notifications");
  103. mRemovedNotification = sbn;
  104. }
  105. private void updateCurrentNotifications() {
  106. try {
  107. StatusBarNotification[] activeNos = getActiveNotifications();
  108. if (mCurrentNotifications.size() == 0) {
  109. mCurrentNotifications.add(null);
  110. }
  111. mCurrentNotifications.set(0, activeNos);
  112. mCurrentNotificationsCounts = activeNos.length;
  113. } catch (Exception e) {
  114. logNLS("Should not be here!!");
  115. e.printStackTrace();
  116. }
  117. }
  118. //获取当前状态栏显示通知总数
  119. public static StatusBarNotification[] getCurrentNotifications() {
  120. if (mCurrentNotifications.size() == 0) {
  121. logNLS("mCurrentNotifications size is ZERO!!");
  122. return null;
  123. }
  124. return mCurrentNotifications.get(0);
  125. }
  126. private static void logNLS(Object object) {
  127. Log.i(TAG, TAG_PRE + object);
  128. }
  129. }

而MainActivity主要负责界面显示与交互,关键代码如下:

[java] view plaincopy

  1. public class MainActivity extends Activity {
  2. private static final String TAG = "SevenNLS";
  3. private static final String TAG_PRE = "["+MainActivity.class.getSimpleName()+"] ";
  4. private static final int EVENT_SHOW_CREATE_NOS = 0;
  5. private static final int EVENT_LIST_CURRENT_NOS = 1;
  6. private static final String ENABLED_NOTIFICATION_LISTENERS = "enabled_notification_listeners";
  7. private static final String ACTION_NOTIFICATION_LISTENER_SETTINGS = "android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS";
  8. private boolean isEnabledNLS = false;
  9. private TextView mTextView;
  10. private Handler mHandler = new Handler() {
  11. @Override
  12. public void handleMessage(Message msg) {
  13. switch (msg.what) {
  14. case EVENT_SHOW_CREATE_NOS:
  15. //显示创建的Notification对应的pkgName、Tag、Id
  16. showCreateNotification();
  17. break;
  18. case EVENT_LIST_CURRENT_NOS:
  19. //显示当前所有的Notification数量及其包名
  20. listCurrentNotification();
  21. break;
  22. default:
  23. break;
  24. }
  25. }
  26. };
  27. @Override
  28. protected void onCreate(Bundle savedInstanceState) {
  29. super.onCreate(savedInstanceState);
  30. setContentView(R.layout.activity_main);
  31. mTextView = (TextView) findViewById(R.id.textView);
  32. }
  33. @Override
  34. protected void onResume() {
  35. super.onResume();
  36. //判断是否有开启Notification access
  37. isEnabledNLS = isEnabled();
  38. logNLS("isEnabledNLS = " + isEnabledNLS);
  39. if (!isEnabledNLS) {
  40. //如果没有开启则显示确认对话框
  41. showConfirmDialog();
  42. }
  43. }
  44. public void buttonOnClicked(View view) {
  45. mTextView.setTextColor(Color.BLACK);
  46. switch (view.getId()) {
  47. case R.id.btnCreateNotify:
  48. logNLS("Create notifications...");
  49. //创建可清除的Notification
  50. createNotification(this);
  51. //显示当前状态栏中所有Notification数量及其包名
  52. mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_SHOW_CREATE_NOS), 50);
  53. break;
  54. case R.id.btnClearLastNotify:
  55. logNLS("Clear Last notification...");
  56. //清除最后一个Notification
  57. clearLastNotification();
  58. //显示当前状态栏中所有Notification数量及其包名
  59. mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_LIST_CURRENT_NOS), 50);
  60. break;
  61. case R.id.btnClearAllNotify:
  62. logNLS("Clear All notifications...");
  63. //清除所有"可被清除"的Notification
  64. clearAllNotifications();
  65. mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_LIST_CURRENT_NOS), 50);
  66. break;
  67. case R.id.btnListNotify:
  68. logNLS("List notifications...");
  69. listCurrentNotification();
  70. break;
  71. case R.id.btnEnableUnEnableNotify:
  72. logNLS("Enable/UnEnable notification...");
  73. //打开Notification access启动/取消界面
  74. openNotificationAccess();
  75. break;
  76. default:
  77. break;
  78. }
  79. }
  80. //......省略
  81. }

4. NotificationListenerService使用过程中有哪些注意事项?

如果细心察看代码的童鞋,一定发现代码中有使用Handler,以及一些奇怪但又被注释掉的代码,比如"a.equals("b")"。从使用上来说,没有必要使用handler,那干嘛要多次一举?这里就给大家分享一下在写NotificationListenerDemo时遇到的一些坑。

①. NotificationMonitor的onCreate方法中使用handler来调用getActiveNotifications()方法

若直接在onCreate或者onBind方法中调用getActiveNotifications()方法是无法获取当前系统通知。主要是因为NotificationMonitor还未完成初始化,而根本原因则是INotificationListenerWrapper对象mWrapper还未初始化,此时使用getActiveNotifications()方法又会调用到mWrapper,因此无法返回正常数据。在NotificationListenerService中可以看到getActiveNotifications()的源码:

[java] view plaincopy

  1. public StatusBarNotification[] getActiveNotifications() {
  2. try {
  3. return getNotificationInterface().getActiveNotificationsFromListener(mWrapper);
  4. } catch (android.os.RemoteException ex) {
  5. Log.v(TAG, "Unable to contact notification manager", ex);
  6. }
  7. return null;
  8. }

也就是说只要在onBind方法完成之后,再调用getActiveNotifications()方法就可以正常获取数据了,因此这里使用了handler多线程的方式。当然,为了保险可以使用sendEmptyMessgeDelay加上延时。

②. 如果NotificationMonitor在onCreate或onBind方法中crash,则该service已经失效,需重启手机才能进行后续开发验证

如果在onCreate或者onBind方法中,出现异常导致NotificationMonitor发生crash,就算找到问题并将其改正,之后的验证还是无法继续进行的,也就是无法收到通知的新增和删除消息,onNotificationPosted和onNotificationRemoved方法不会被调用。

这也是我在onBind方法中故意注释导致空指针异常的代码,有兴趣的童鞋可以把注释去掉后尝试,去掉注释会导致NotificationListenerDemo异常停止,此时你再加上注释再次运行NotificationListenerDemo,虽然程序可以正常启动,但无法正常执行NotificationMonitor中的onNotificationPosted和onNotificationRemoved方法。这个涉及NotificationListenerService的原理,后面会另行分析。

③. MainActivity中onClick方法里使用handler操作

当点击删除通知时,系统通知相关状态还未更新,此时还没有回调到NotificationMonitor中,所以获取的数据就还是上一次的数据。为了能够获取到正确的Notification数据,可以使用handler并加上延时,这样再去获取Notification信息时,系统已经触发了NotificationMonitor回调,数据也有正常了。另外,50ms的延时几乎是感知不到的。

④. 为什么要使用ArrayList来保存StatusBarNotification数组对象

当新增或者删除通知时,会触发onNotificationPosted或onNotificationRemoved回调,在该方法中调用getActiveNotifications()方法用以获取当前系统通知信息。而getActiveNotifications()返回的是StatusBarNotification[]数组,因为这个数组是可变长的,也就是长度会随时变化,因此无法直接存储。使用ArrayList可以很好的解决这个问题,在ArrayList对象中添加一个StatusBarNotification[]对象,之后使用ArrayList.set(0,statusbar[])方法对数据进行更新即可。

总结

NotificationListenerService是Android 4.3 之后新增的接口服务,用于获取系统Notification信息,这在之前的Android版本是无法直接办到的。在Android 4.4中,增加了Notification.extra变量,使得获取Notification相关信息更加丰富,这些接口的开放更加利于三方应用的使用,但同时也会带来一些隐私问题。

本文针对NotificationListenerService的使用进行了详细分析,当然其中不乏有失偏颇的地方,本着互联网知识共享精神也将自己的一些记录发布出来,一来可做笔记,二来希望能够给苦苦寻觅的童鞋一些帮助。

后续会对NotificationListenerService的原理进行分析,敬请期待。

NotificationMonitor代码免积分下载:下载Demo

为了后续能够更新,已经代码传到github上,有兴趣的童鞋可以在github上查看,连接戳这里

相关文章
  • NotificationManagerService使用详解与原理分析(二) 2015-05-12

    前置文章: <Android 4.4 KitKat NotificationManagerService使用详解与原理分析(一)__使用详解> 转载请务必注明出处:http://blog.csdn.net/yihongyuelan 概况 在上一篇文章<Android 4.4 KitKat NotificationManagerService使用详解与原理分析(一)__使用详解>中详细介绍了NotificationListenerService的使用方法,以及在使用过程中遇到的问题和

  • NotificationManagerService使用详解与原理分析(一) 2015-05-12

    概况 Android在4.3的版本中(即API 18)加入了NotificationListenerService,根据SDK的描述(AndroidDeveloper)可以知道,当系统收到新的通知或者通知被删除时,会触发NotificationListenerService的回调方法.同时在Android 4.4 中新增了Notification.extras 字段,也就是说可以使用NotificationListenerService获取系统通知具体信息,这在以前是需要用反射来实现的. 转载请

  • (二)代理模式详解(包含原理详解) 2015-01-21

    LZ不希望写的东西与网络上的资料千篇一律,所以这一系列不会像很多典型文章一章,只是列出这个模式的定义以及一堆适用的情况,然后就是一堆这个模式的各个角色,对于这种罗列LZ并不反对,相比之下会比较清晰,但是如果脱离了实际,就会导致看的人特别是初学者觉得设计模式很陌生很遥远. LZ并不反对这种教学式的标准模式,但说实话,LZ本人看这种帖子从来都感觉收获不大,看一遍看一遍,到现在都没记住那些各个适用的情况与一堆乱七八糟的角色. 所以LZ探讨代理模式,不会再按这个步骤进行,而是跟着自己的思维进行. 首先代

  • apache日志文件详解和实用分析命令 2015-02-04

    这篇文章主要介绍了apache日志文件每条数据的请意义,以及一些实用日志分析命令,需要的朋友可以参考下 一.日志分析 如果apache的安装时采用默认的配置,那么在/logs目录下就会生成两个文件,分别是access_log和error_log 1).access_log access_log为访问日志,记录所有对apache服务器进行请求的访问,它的位置和内容由CustomLog指令控制,LogFormat指令可以用来简化该日志的内容和格式 例如,我的其中一台服务器配置如下: CustomLo

  • iptables详解 2013-02-22

    iptables详解 Iptables原理 现在防火墙主要分以下三种类型: 包过滤.应用代理.状态检测 包过滤防火墙:现在静态包过滤防火墙市面上已经看不到了,取而代之的是动态包过滤技术的防火墙哈~ 代理防火墙:因一些特殊的报文攻击可以轻松突破包过滤防火墙的保护,比如大家知道的SYN攻击.ICMP洪水攻击,所以以代理服务器作为专门为用户保密或者突破访问限制的数据转发通道的应用代理防火墙出现了哈~其使用了一种应用协议分析的新技术. 状态检测防火墙:其基于动态包过滤技术发展而来,加入了一种状态检测的模

  • PHP 采集大全 采集原理分析 禁用采集 各种采集方法详解 采集的攻于防 采集性能 应用协议分析 2014-03-11

    //py by http://my.codeweblog.com/cart 做了N年的PHP,采集了N家数据,由初学者菜鸟,到现在的熟手,采集天猫.淘宝.腾讯.京东.敦煌.Lightinthebox.大龙.zencart.magento.prestashop.opencart.xcart.踏踏....对采集颇有诸多的理解. 现在给大家分析下,如有误,请指出. 我能想到的常用采集方法: 1. file 支持应用层协议,返回的数据是以数组形式返回,需要开启allow_url_fopen. 长处:擅长

  • jQuery中extend函数的实现原理详解 2013-11-14

    这篇文章主要介绍了jQuery中extend函数的实现原理详解,extend()函数用于jQuery插件的开发,本文主要分析它的实现原理,需要的朋友可以参考下 extend()是jQuery中一个重要的函数,作用是实现对对象的扩展, 它经常用于jQuery插件的开发,jQuery内部也使用它来扩展属性方法,如上篇文章中讲到的noConflict方法,就是用extend方法来扩展的. 在jQuery的API手册中,我们看到,extend实际上是挂载在jQuery和jQuery.fn上的两个不同方法

  • php urlencode()与urldecode()函数字符编码原理详解 2014-05-24

    中文字符编码研究系列第五期,详解 urlencode()与urldecode()函数字符编码原理,两个函数分别用于编码 URL 字符串和解码已编码的 URL 字符串,实现对中文字符的编码 其原理就是把中文字符转换为十六进制并按某种规则进行字符串组合,实现字符的编码与解编码,保证URL数据传递过程中字符的完整性和兼容性,主要讨论中文字符的编码情况. 一,FireFox浏览器编码中文字符 在Firefox浏览器下如果输入中文字符,将会自动实现URL编码,如下 按下Enter键前 按下Enter键后

  • 基于一个应用程序多线程误用的分析详解 2014-11-26

    本篇文章是对一个应用程序多线程的误用进行了详细的分析介绍,需要的朋友参考下 一.需求和初步实现很简单的一个windows服务:客户端连接邮件服务器,下载邮件(含附件)并保存为.eml格式,保存成功后删除服务器上的邮件.实现的伪代码大致如下: public void Process() { var recordCount = 1000;//每次取出邮件记录数 while (true) { using (var client = new Pop3Client()) { //1.建立连接,并进行身份认

  • 企业网站架构之Nginx详解原理以及工作模块:源码Lnmp架构 2014-08-11

    Nginx详解及lnmp环境架构 一.Nginx详解以及优点 在当前互联网环境下,一般高端的服务前端都采用nginx作为web前端,而更多的都是采用lnmp架构,真正的后端服务器才会采用apache. 为什么这么做,要取决于nginx和apache两者之间的优缺性.: nginx与apache相比有以下优势:在性能上,nginx占用很少的系统资源,能支持更多的并发链接,达到更高的访问率;在功能上,Nginx是优秀的代理服务器和负载均衡器;在安装配置上,简单灵活. nginx模块基本都是静态编译,

  • Linux 日志分析工具之awstats详解 2014-10-13

    一.前言 二.awstats 简介 三.awstats 特点 四.awstats 运行原理 五.awstats 安装与配置详解 六.awstats 执行日志分析 七.awstats 进行多站点日志分析 八.awstats 问题汇总 IP 地址国家.区域显示问题 中文乱码问题 九.awstats 总结 注,操作系统 CentOS 6.4 x86_64,软件版本 awstats 7.2(稳定版),软件下载 http://sourceforge.net/projects/awstats/ . 一.前言

  • 酷炫开源项目cardsui-for-android-超详细源码分析,详解所用特效是如何实现的 2014-10-22

    cardsui-for-android的下载地址https://github.com/Androguide/cardsui-for-android 以下是我截取的2个图片,可以自定义成Card形式的View,布局可以自己设定.点击露出来的部分可以使点击的Card滑落到下面,也可以左右滑动删除Card.效果非常好 这篇文章主要写下通过源码分析一下几个地方是怎么实现的. Card的View和布局 相互叠层的card 点击翻转下滑 左右移动删除条目 可以根据需求自行决定观看,哈哈 从git下载导入项目

  • php 分页原理详解 2014-02-02

    分页原理详解,其实各个语言的都差不多,主要是程序跟数据库的表达方式不一样. 在看本文之前,请确保你已掌握了PHP的一些知识以及MYSQL的查询操作基础哦. 作为一个Web程序,经常要和不计其数的数据打交道,比如会员的数据,文章数据,假如只有几十个会员那很好办,在一页显示就可以了,可是假如你的网站是几千甚至几十万会员的话,如果都在一页打开的话无论对浏览器还是观看者都是一种折磨,而且如果数据上亿,从数据库里查询一次的话,对服务器的压力是很大的,这不是正确的方法. 相信每个学习PHP的新手都会对分页这

  • 高性能JavaScript模板引擎实现原理详解 2014-03-01

    这篇文章主要介绍了JavaScript模板引擎实现原理详解,本文着重讲解artTemplate模板的实现原理,它采用预编译方式让性能有了质的飞跃,是其它知名模板引擎的25.32 倍,需要的朋友可以参考下 随着 web 发展,前端应用变得越来越复杂,基于后端的 javascript(Node.js) 也开始崭露头角,此时 javascript 被寄予了更大的期望,与此同时 javascript MVC 思想也开始流行起来.javascript 模板引擎作为数据与界面分离工作中最重要一环,越来越受开

  • MySQL死锁问题分析及解决方法实例详解 2014-07-22

    这篇文章主要介绍了MySQL死锁问题分析及解决方法,需要的朋友可以参考下 MySQL死锁问题是很多程序员在项目开发中常遇到的问题,现就MySQL死锁及解决方法详解如下: 1.MySQL常用存储引擎的锁机制 MyISAM和MEMORY采用表级锁(table-level locking) BDB采用页面锁(page-level locking)或表级锁,默认为页面锁 InnoDB支持行级锁(row-level locking)和表级锁,默认为行级锁 2.各种锁特点 表级锁:开销小,加锁快;不会出现死

  • javaScript中的this示例学习详解及工作原理 2014-08-24

    这篇文章主要介绍了javaScript中的this示例学习详解及工作原理,大家参考使用吧 this的工作原理 如果一个函数被作为一个对象的方法调用,那么this将被指派为这个对象. var parent = { method: function () { console.log(this); } }; parent.method(); // <- parent 注意这种行为非常"脆弱",如果你获取一个方法的引用并且调用,那么this的值不会是parent了,而是window全局对

  • 使用Log4j为项目配置日志输出应用详解以及示例演示的实现分析 2014-12-29

    本篇文章是对Log4j为项目配置日志输出应用详解以及示例演示的实现进行了分析介绍,需要的朋友参考下 Log4j组件构成 Log4j由三个重要的组件构成: 1.日志信息的优先级(Logger) 2.日志信息的输出目的地(Appender) 3.日志信息的输出格式(Layout). 概要: 日志信息的优先级从高到低有ERROR.WARN. INFO.DEBUG,分别用来指定这条日志信息的重要程度: 日志信息的输出目的地指定了日志将打印到控制台还是文件中: 而输出格式则控制了日志信息的显示内容. Lo

  • 详解分析开心网花园"蜂蝶卡"道具 2014-10-18

    邀请链接: 山寨版开心网kaixin.com的注册邀请 开心网kaixin001.com注册详解和邀请码发放 我最近玩开心网花园的热情很是瞒高的,经常晚上12点的时候出去偷别人的果实.我自认为上了学之后,基本上没有干过什么坏事了,现在开心网的花园有了"偷菜"的功能,何乐而不为呢?自从推出"蜂蝶卡"道具之后,更多的开心人更有"激情",我也不例外.但是我们怎么样使用以及利用好"蜂蝶卡"这个道具呢?请先看"蜂蝶卡"

  • 负载均衡原理与实践详解 第一篇 2012-04-05

    系列文章: 负载均衡原理与实践详解 第一篇(重新整理) 负载均衡原理与实践详解 第二篇(重新整理) 负载均衡原理与实践详解 第三篇 服务器负载均衡的基本概念-网络基础 负载均衡在服务器和网络的世界中并不是一个新的概念,许多产品都能够提供不同类型的负载均衡解决方案.比如,路由器能够在不同的路径之间分配流量到达相同的目的地,在不同的网络资源中平衡它们的负担.另一方面,一个服务器负载均衡设备,能在多台服务器之间分发流量. 最初,负载均衡设备只是满足简单的负载均衡需求,而如今这些产品已得到迅速的发展,能

  • Java中GC的工作原理详解 2012-04-28

    一个优秀的Java程序员必须了解GC的工作原理.如何优化GC的性能.如何与GC进行有限的交互,因为有一些应用程序对性能要求较高,例如嵌入式系统.实时系统等,只有全面提升内存的管理效率 ,才能提高整个应用程序的性能.本篇文章首先简单介绍GC的工作原理之后,然后再对GC的几个关键问题进行深入探讨,最后提出一些Java程序设计建议,从GC角度提高Java程序的性能. GC的基本原理 Java的内存管理实际上就是对象的管理,其中包括对象的分配和释放. 对于程序员来说,分配对象使用new关键字;释放对象时