日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪(fǎng)問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

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

發(fā)布時(shí)間:2023/12/9 编程问答 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 NotificationManagerService使用详解与原理分析(一) 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

概況

? ? ? ??Android在4.3的版本中(即API 18)加入了NotificationListenerService,根據(jù)SDK的描述(AndroidDeveloper)可以知道,當(dāng)系統(tǒng)收到新的通知或者通知被刪除時(shí),會(huì)觸發(fā)NotificationListenerService的回調(diào)方法。同時(shí)在Android 4.4 中新增了Notification.extras 字段,也就是說(shuō)可以使用NotificationListenerService獲取系統(tǒng)通知具體信息,這在以前是需要用反射來(lái)實(shí)現(xiàn)的。

轉(zhuǎn)載請(qǐng)務(wù)必注明出處:http://blog.csdn.net/yihongyuelan

重要關(guān)系

? ? ? ? 對(duì)于系統(tǒng)通知,三方APP使用NotificationListenerService主要目的是為了獲取系統(tǒng)通知相關(guān)信息,主要包括:通知的新增和刪除,獲取當(dāng)前通知數(shù)量,通知內(nèi)容相關(guān)信息等。這些信息可以通過(guò)NotificationListenerService類(lèi)提供的方法以及StatusBarNotification類(lèi)對(duì)象來(lái)獲取。

NotificationListenerService主要方法(成員變量):

cancelAllNotifications()?:刪除系統(tǒng)中所有可被清除的通知;?
cancelNotification(String pkg, String tag, int id)?:刪除具體某一個(gè)通知;
getActiveNotifications()?:返回當(dāng)前系統(tǒng)所有通知到StatusBarNotification[];
onNotificationPosted(StatusBarNotification sbn)?:當(dāng)系統(tǒng)收到新的通知后出發(fā)回調(diào);?
onNotificationRemoved(StatusBarNotification sbn)?:當(dāng)系統(tǒng)通知被刪掉后出發(fā)回調(diào);

以上是NotificationListenerService的主要方法,通過(guò)這些方法就可以在應(yīng)用中操作系統(tǒng)通知,在NotificationListenerService中除了對(duì)通知的操作之外,還可以獲取到通知的StatusBarNotification對(duì)象,通過(guò)該對(duì)象可以獲取通知更詳細(xì)的數(shù)據(jù)。

StatusBarNotification主要方法(成員變量):

getId():返回通知對(duì)應(yīng)的id;
getNotification():返回通知對(duì)象;
getPackageName():返回通知對(duì)應(yīng)的包名;
getPostTime():返回通知發(fā)起的時(shí)間;
getTag():返回通知的Tag,如果沒(méi)有設(shè)置返回null;
getUserId():返回UserId,用于多用戶(hù)場(chǎng)景;
isClearable():返回該通知是否可被清楚,FLAG_ONGOING_EVENT、FLAG_NO_CLEAR;
isOngoing():檢查該通知的flag是否為FLAG_ONGOING_EVENT;

使用簡(jiǎn)介

正確使用NotificationListenerService需要注意三點(diǎn):

(1). 新建一個(gè)類(lèi)并繼承自NotificationListenerService,override其中重要的兩個(gè)方法;

[java]?view plaincopy

  • public?class?NotificationMonitor?extends?NotificationListenerService?{??

  • ????????@Override??

  • ????????public?void?onNotificationPosted(StatusBarNotification?sbn)?{??

  • ??????????????Log.i("SevenNLS","Notification?posted");??

  • ????????}??

  • ???

  • ????????@Override??

  • ????????public?void?onNotificationRemoved(StatusBarNotification?sbn)?{??

  • ??????????????Log.i("SevenNLS","Notification?removed");???

  • ????????}??

  • }??

  • (2). 在AndroidManifest.xml中注冊(cè)Service并聲明相關(guān)權(quán)限;

    [html]?view plaincopy

  • <service?android:name=".NotificationMonitor"??

  • ?????????android:label="@string/service_name"??

  • ?????????android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">??

  • ????<intent-filter>??

  • ????????<action?android:name="android.service.notification.NotificationListenerService"?/>??

  • ????</intent-filter>??

  • </service>??

  • (3). 開(kāi)啟NotificationMonitor的監(jiān)聽(tīng)功能;

    ? ? ? ??完成以上兩步之后,將程序編譯并安裝到手機(jī)上,但此時(shí)該程序是無(wú)法監(jiān)聽(tīng)到新增通知和刪除通知的,還需要在"Settings > Security > Notification access"中,勾選NotificationMonitor。此時(shí)如果系統(tǒng)收到新的通知或者通知被刪除就會(huì)打印出相應(yīng)的log了。

    ? ? ? ??這里需要注意,如果手機(jī)上沒(méi)有安裝使用NotificationListenerService類(lèi)的APP,Notification access是不會(huì)顯示出來(lái)的。可以在源碼/packages/apps/Settings/src/com/android/settings/SecuritySettings.java中看到,如果沒(méi)有使用NotificationListenerService的APK,直接就不顯示這一項(xiàng)了。

    [java]?view plaincopy

  • mNotificationAccess?=?findPreference(KEY_NOTIFICATION_ACCESS);??

  • if?(mNotificationAccess?!=?null)?{??

  • ????final?int?total?=?NotificationAccessSettings.getListenersCount(mPM);??

  • ????if?(total?==?0)?{??

  • ????????if?(deviceAdminCategory?!=?null)?{??

  • ????????????deviceAdminCategory.removePreference(mNotificationAccess);??

  • ????????}??

  • ????}?else?{??

  • ????????final?int?n?=?getNumEnabledNotificationListeners();??

  • ????????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)));??

  • ????????}??

  • ????}??

  • }??

  • 使用詳解

    通過(guò)前面的講解(實(shí)際上就是對(duì)AndroidDeveloper的解釋),已經(jīng)可以正常使用NotificationListenerService了,但對(duì)于實(shí)際應(yīng)用中,需要考慮的事情還比較多。比如:

    1. 如何檢測(cè)應(yīng)用已開(kāi)啟Notification access監(jiān)聽(tīng)功能?

    如果檢測(cè)到應(yīng)用沒(méi)有激活Notification access監(jiān)聽(tīng)功能,需要提示用戶(hù)開(kāi)啟;

    2. 能不能主動(dòng)跳轉(zhuǎn)到Notification access監(jiān)聽(tīng)頁(yè)面?

    如果能夠根據(jù)第1步的判斷自動(dòng)跳轉(zhuǎn)到對(duì)應(yīng)的頁(yè)面,那可以省掉很多操作;

    3. 如何與NotificationListenerService交互?

    涉及到與Service的交互,但又與普通的Service不同,這里后文解釋;

    4. NotificationListenerService使用過(guò)程中有哪些注意事項(xiàng)?

    在使用NotificationListenerService過(guò)程中自己遇到了一些坑,后文會(huì)通過(guò)分析給出相應(yīng)的解決方案;

    程序運(yùn)行截圖

    ?

    圖 1 程序運(yùn)行截圖

    示例介紹

    ? ? ? ??NotificationListenerDemo主要用于獲取系統(tǒng)當(dāng)前通知信息,并可手動(dòng)創(chuàng)建"可清除通知",逐條刪除"可清除通知",一次性刪除"可清除通知",以及顯示系統(tǒng)當(dāng)前活動(dòng)的通知信息。實(shí)際上該示例回答了前面使用詳解中提出的各項(xiàng)疑問(wèn),在實(shí)際使用過(guò)程中相信大部分人都會(huì)遇到,因此這里逐條展開(kāi)與大家分享。


    圖 2 主界面

    功能分析

    1. 如何檢測(cè)應(yīng)用已開(kāi)啟Notification access監(jiān)聽(tīng)功能?

    ? ? ? ??在程序啟動(dòng)時(shí),執(zhí)行Notification access的檢測(cè),查看是否訪(fǎng)問(wèn)Notification的權(quán)限。如果用戶(hù)沒(méi)有Enable Notification access,則彈出提示對(duì)話(huà)框,點(diǎn)擊OK跳轉(zhuǎn)到Notification access設(shè)置頁(yè)面。


    圖 3 首次啟動(dòng) isEnable

    ? ? ? ??使用NotificationListenerService的應(yīng)用如果開(kāi)啟了Notification access,系統(tǒng)會(huì)將包名等相關(guān)信息寫(xiě)入SettingsProver數(shù)據(jù)庫(kù)中,因此可以從數(shù)據(jù)庫(kù)中獲取相關(guān)信息并過(guò)濾,從而判斷應(yīng)用是否開(kāi)啟了Notification access,代碼如下:

    [java]?view plaincopy

  • private?static?final?String?ENABLED_NOTIFICATION_LISTENERS?=?"enabled_notification_listeners";??

  • private?boolean?isEnabled()?{??

  • ????String?pkgName?=?getPackageName();??

  • ????final?String?flat?=?Settings.Secure.getString(getContentResolver(),??

  • ????????????ENABLED_NOTIFICATION_LISTENERS);??

  • ????if?(!TextUtils.isEmpty(flat))?{??

  • ????????final?String[]?names?=?flat.split(":");??

  • ????????for?(int?i?=?0;?i?<?names.length;?i++)?{??

  • ????????????final?ComponentName?cn?=?ComponentName.unflattenFromString(names[i]);??

  • ????????????if?(cn?!=?null)?{??

  • ????????????????if?(TextUtils.equals(pkgName,?cn.getPackageName()))?{??

  • ????????????????????return?true;??

  • ????????????????}??

  • ????????????}??

  • ????????}??

  • ????}??

  • ????return?false;??

  • }??

  • 在返回值flat中如果包含了應(yīng)用的包名,即可確定應(yīng)用已開(kāi)啟Notification access,反之則表示沒(méi)有開(kāi)啟。

    2. 能不能主動(dòng)跳轉(zhuǎn)到Notification access監(jiān)聽(tīng)頁(yè)面?

    ? ? ? ??通過(guò)查看可以知道,Notification access界面接收action為"android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS"的intent啟動(dòng),因此使用startActivity可以很容易的跳轉(zhuǎn)到該頁(yè)面,從而避免用戶(hù)在Settings中查找。代碼如下:

    [java]?view plaincopy

  • private?static?final?String?ACTION_NOTIFICATION_LISTENER_SETTINGS?=?"android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS";??

  • private?void?openNotificationAccess()?{??

  • ????startActivity(new?Intent(ACTION_NOTIFICATION_LISTENER_SETTINGS));??

  • }??

  • 3. 如何與NotificationListenerService交互?

    ? ? ? ??因?yàn)镹otificationListenerService中包含了四個(gè)重要的方法,分別是:onNotificationPosted、onNotificationRemoved、cancelNotification、cancelAllNotifications。通過(guò)這些方法我們才能實(shí)現(xiàn)諸如通知信息的獲取以及刪除等功能,雖然這些方法是public的,那是不是意味著我們只要拿到NotificationListenerService的對(duì)象就可以直接調(diào)用這些方法了呢?那如何拿到Service的對(duì)象呢?在之前的博文中,曾有提到與Service的交互( 具體可參考拙作《Android中程序與Service交互的方式——交互方式》),可以看到與Service的交互有很多種方法,但如果要拿到Service的對(duì)象,歸根到底還是需要Binder。

    ? ? ? ??也就是說(shuō)得使用bindService的辦法,將onServiceConnected回調(diào)中的IBinder對(duì)象轉(zhuǎn)型成NotificationListenerService的對(duì)象。測(cè)試代碼如下:

    [java]?view plaincopy

  • //在MainActivity.java的onCreate方法中使用bindService幫頂NotificationMonitor服務(wù)??

  • bindService(new?Intent(this,NotificationMonitor.class??),?new?ServiceConnection()?{??

  • ??@Override??

  • ??public?void?onServiceDisconnected(ComponentName?arg0)?{??

  • ??}??

  • ????

  • ??@Override??

  • ??public?void?onServiceConnected(ComponentName?arg0,?IBinder?arg1)?{??

  • ????NotificationMonitor.MyBinder?localBinder?=?(MyBinder)arg1;??

  • ????NotificationMonitor?mMonitor?=?localBinder.getService();??

  • ??}??

  • },?BIND_AUTO_CREATE);??

  • [java]?view plaincopy

  • //NotificationMonitor的onBind方法返回構(gòu)造的Binder對(duì)象??

  • public?class?NotificationMonitor?extends?NotificationListenerService?{??

  • ??private?MyBinder?mBinder?=?new?MyBinder();??

  • ??public??class?MyBinder?extends?Binder{??

  • ????public?NotificationMonitor?getService(){??

  • ??????return?NotificationMonitor.this;??

  • ????}??

  • ??}??

  • ??

  • ??@Override??

  • ??public?IBinder?onBind(Intent?arg0)?{??

  • ????return?mBinder;??

  • ??}??

  • ??

  • ??@Override??

  • ??public?void?onNotificationPosted(StatusBarNotification?sbn)?{??

  • ????getActiveNotifications();??

  • ????cancelAllNotifications();??

  • ??}??

  • ??

  • ??@Override??

  • ??public?void?onNotificationRemoved(StatusBarNotification?sbn)?{??

  • ??}??

  • }??

  • 那這樣操作之后是不是就意味著可以拿到NotificationMonitor的對(duì)象并直接調(diào)用getActiveNotifications()方法,用于獲取當(dāng)前系統(tǒng)通知的信息了呢?很抱歉,事實(shí)證明這樣是不行的。這里簡(jiǎn)單的分析下,在后面的NotificationListenerService原理分析中再詳細(xì)講解。在NotificationListenerService的源碼中可以看到:

    [java]?view plaincopy

  • @Override??

  • public?IBinder?onBind(Intent?intent)?{??

  • ????if?(mWrapper?==?null)?{??

  • ????????mWrapper?=?new?INotificationListenerWrapper();??

  • ????}?????

  • ????return?mWrapper;??

  • }??

  • 這里的INotificationListenerWrapper是NotificationListenerService的一個(gè)內(nèi)部類(lèi):

    [java]?view plaincopy

  • private?class?INotificationListenerWrapper?extends?INotificationListener.Stub??

  • 而NotificationMonitor繼承自NotificationListenerService,默認(rèn)的onBind方法卻是:

    [java]?view plaincopy

  • @Override??

  • public?IBinder?onBind(Intent?intent)?{??

  • ????return?super.onBind(intent);??

  • }??

  • ? ? ? ??這里注意,一般情況下service的onBind方法返回要么是null要么是Binder對(duì)象,可這里直接調(diào)用父類(lèi)NotificationListenerService的onBind方法,而父類(lèi)返回的是INotificationListenerWrapper的對(duì)象。這說(shuō)明Binder對(duì)象已經(jīng)被指定了,不能再給NotificationMonitor指定其它的Binder對(duì)象。如果你非要給NotificationMonitor指定其它的Binder對(duì)象,那么就無(wú)法使用INotificationListenerWrapper提供的方法。也就是說(shuō)要么就用系統(tǒng)NotificationListenerService提供的方法,要么就把NotificationMonitor當(dāng)一個(gè)普通的Service來(lái)用,系統(tǒng)提供的方法都不能使用。

    ? ? ? ??那應(yīng)該如何使用NotificationListenerService中的方法呢?在拙作《Android中程序與Service交互的方式——交互方式》中,已經(jīng)提供了很多的例子,這里僅以廣播的方式為例。

    ? ? ? ??既然NotificationMonitor可以使用NotificationListenerService的方法,那通過(guò)NotificationMonitor把通知狀態(tài)的改變以及數(shù)據(jù)獲取到,并使用static數(shù)據(jù)進(jìn)行存儲(chǔ),之后再在MainActivity中直接使用即可。在MainActivity中控制通知的單個(gè)刪除和全部刪除,則使用廣播的方式發(fā)送給NotificationMonitor進(jìn)行處理。MainActivity與NotificationMonitor的關(guān)系類(lèi)圖如下:

    圖 4 結(jié)構(gòu)類(lèi)圖

    NotificationMonitor和MainActivity關(guān)鍵代碼如下:

    [java]?view plaincopy

  • public?class?NotificationMonitor?extends?NotificationListenerService?{??

  • ????private?static?final?String?TAG?=?"SevenNLS";??

  • ????private?static?final?String?TAG_PRE?=?"["?+?NotificationMonitor.class.getSimpleName()?+?"]?";??

  • ????private?static?final?int?EVENT_UPDATE_CURRENT_NOS?=?0;??

  • ????public?static?final?String?ACTION_NLS_CONTROL?=?"com.seven.notificationlistenerdemo.NLSCONTROL";??

  • ????//用于存儲(chǔ)當(dāng)前所有的Notification的StatusBarNotification對(duì)象數(shù)組??

  • ????public?static?List<StatusBarNotification[]>?mCurrentNotifications?=?new?ArrayList<StatusBarNotification[]>();??

  • ????public?static?int?mCurrentNotificationsCounts?=?0;??

  • ????//收到新通知后將通知的StatusBarNotification對(duì)象賦值給mPostedNotification??

  • ????public?static?StatusBarNotification?mPostedNotification;??

  • ????//刪除一個(gè)通知后將通知的StatusBarNotification對(duì)象賦值給mRemovedNotification??

  • ????public?static?StatusBarNotification?mRemovedNotification;??

  • ????private?CancelNotificationReceiver?mReceiver?=?new?CancelNotificationReceiver();??

  • ????//?String?a;??

  • ????private?Handler?mMonitorHandler?=?new?Handler()?{??

  • ????????@Override??

  • ????????public?void?handleMessage(Message?msg)?{??

  • ????????????switch?(msg.what)?{??

  • ????????????????case?EVENT_UPDATE_CURRENT_NOS:??

  • ????????????????????updateCurrentNotifications();??

  • ????????????????????break;??

  • ????????????????default:??

  • ????????????????????break;??

  • ????????????}??

  • ????????}??

  • ????};??

  • ??

  • ????class?CancelNotificationReceiver?extends?BroadcastReceiver?{??

  • ??

  • ????????@Override??

  • ????????public?void?onReceive(Context?context,?Intent?intent)?{??

  • ????????????String?action;??

  • ????????????if?(intent?!=?null?&&?intent.getAction()?!=?null)?{??

  • ????????????????action?=?intent.getAction();??

  • ????????????????if?(action.equals(ACTION_NLS_CONTROL))?{??

  • ????????????????????String?command?=?intent.getStringExtra("command");??

  • ????????????????????if?(TextUtils.equals(command,?"cancel_last"))?{??

  • ????????????????????????if?(mCurrentNotifications?!=?null?&&?mCurrentNotificationsCounts?>=?1)?{??

  • ????????????????????????????//每次刪除通知最后一個(gè)??

  • ????????????????????????????StatusBarNotification?sbnn?=?getCurrentNotifications()[mCurrentNotificationsCounts?-?1];??

  • ????????????????????????????cancelNotification(sbnn.getPackageName(),?sbnn.getTag(),?sbnn.getId());??

  • ????????????????????????}??

  • ????????????????????}?else?if?(TextUtils.equals(command,?"cancel_all"))?{??

  • ????????????????????????//刪除所有通知??

  • ????????????????????????cancelAllNotifications();??

  • ????????????????????}??

  • ????????????????}??

  • ????????????}??

  • ????????}??

  • ??

  • ????}??

  • ??

  • ????@Override??

  • ????public?void?onCreate()?{??

  • ????????super.onCreate();??

  • ????????logNLS("onCreate...");??

  • ????????IntentFilter?filter?=?new?IntentFilter();??

  • ????????filter.addAction(ACTION_NLS_CONTROL);??

  • ????????registerReceiver(mReceiver,?filter);??

  • ????//在onCreate時(shí)第一次調(diào)用getActiveNotifications()??

  • ????????mMonitorHandler.sendMessage(mMonitorHandler.obtainMessage(EVENT_UPDATE_CURRENT_NOS));??

  • ????}??

  • ??

  • ????@Override??

  • ????public?void?onDestroy()?{??

  • ????????super.onDestroy();??

  • ????????unregisterReceiver(mReceiver);??

  • ????}??

  • ??

  • ????@Override??

  • ????public?IBinder?onBind(Intent?intent)?{??

  • ????????//?a.equals("b");??

  • ????????logNLS("onBind...");??

  • ????????return?super.onBind(intent);??

  • ????}??

  • ??

  • ????@Override??

  • ????public?void?onNotificationPosted(StatusBarNotification?sbn)?{??

  • ????????//當(dāng)系統(tǒng)收到新的通知后,更新mCurrentNotifications列表??

  • ????????updateCurrentNotifications();??

  • ????????logNLS("onNotificationPosted...");??

  • ????????logNLS("have?"?+?mCurrentNotificationsCounts?+?"?active?notifications");??

  • ????????mPostedNotification?=?sbn;??

  • ????????//通過(guò)以下方式可以獲取Notification的詳細(xì)信息??

  • ????????/*?

  • ?????????*?Bundle?extras?=?sbn.getNotification().extras;?String?

  • ?????????*?notificationTitle?=?extras.getString(Notification.EXTRA_TITLE);?

  • ?????????*?Bitmap?notificationLargeIcon?=?((Bitmap)?

  • ?????????*?extras.getParcelable(Notification.EXTRA_LARGE_ICON));?Bitmap?

  • ?????????*?notificationSmallIcon?=?((Bitmap)?

  • ?????????*?extras.getParcelable(Notification.EXTRA_SMALL_ICON));?CharSequence?

  • ?????????*?notificationText?=?extras.getCharSequence(Notification.EXTRA_TEXT);?

  • ?????????*?CharSequence?notificationSubText?=?

  • ?????????*?extras.getCharSequence(Notification.EXTRA_SUB_TEXT);?

  • ?????????*?Log.i("SevenNLS",?"notificationTitle:"+notificationTitle);?

  • ?????????*?Log.i("SevenNLS",?"notificationText:"+notificationText);?

  • ?????????*?Log.i("SevenNLS",?"notificationSubText:"+notificationSubText);?

  • ?????????*?Log.i("SevenNLS",?

  • ?????????*?"notificationLargeIcon?is?null:"+(notificationLargeIcon?==?null));?

  • ?????????*?Log.i("SevenNLS",?

  • ?????????*?"notificationSmallIcon?is?null:"+(notificationSmallIcon?==?null));?

  • ?????????*/??

  • ????}??

  • ??

  • ????@Override??

  • ????public?void?onNotificationRemoved(StatusBarNotification?sbn)?{??

  • ????????//當(dāng)有通知被刪除后,更新mCurrentNotifications列表??

  • ????????updateCurrentNotifications();??

  • ????????logNLS("removed...");??

  • ????????logNLS("have?"?+?mCurrentNotificationsCounts?+?"?active?notifications");??

  • ????????mRemovedNotification?=?sbn;??

  • ????}??

  • ??

  • ????private?void?updateCurrentNotifications()?{??

  • ????????try?{??

  • ????????????StatusBarNotification[]?activeNos?=?getActiveNotifications();??

  • ????????????if?(mCurrentNotifications.size()?==?0)?{??

  • ????????????????mCurrentNotifications.add(null);??

  • ????????????}??

  • ????????????mCurrentNotifications.set(0,?activeNos);??

  • ????????????mCurrentNotificationsCounts?=?activeNos.length;??

  • ????????}?catch?(Exception?e)?{??

  • ????????????logNLS("Should?not?be?here!!");??

  • ????????????e.printStackTrace();??

  • ????????}??

  • ????}??

  • ??

  • ????//獲取當(dāng)前狀態(tài)欄顯示通知總數(shù)??

  • ????public?static?StatusBarNotification[]?getCurrentNotifications()?{??

  • ????????if?(mCurrentNotifications.size()?==?0)?{??

  • ????????????logNLS("mCurrentNotifications?size?is?ZERO!!");??

  • ????????????return?null;??

  • ????????}??

  • ????????return?mCurrentNotifications.get(0);??

  • ????}??

  • ??

  • ????private?static?void?logNLS(Object?object)?{??

  • ????????Log.i(TAG,?TAG_PRE?+?object);??

  • ????}??

  • ??

  • }??

  • 而MainActivity主要負(fù)責(zé)界面顯示與交互,關(guān)鍵代碼如下:

    [java]?view plaincopy

  • public?class?MainActivity?extends?Activity?{??

  • ??

  • ????private?static?final?String?TAG?=?"SevenNLS";??

  • ????private?static?final?String?TAG_PRE?=?"["+MainActivity.class.getSimpleName()+"]?";??

  • ????private?static?final?int?EVENT_SHOW_CREATE_NOS?=?0;??

  • ????private?static?final?int?EVENT_LIST_CURRENT_NOS?=?1;??

  • ????private?static?final?String?ENABLED_NOTIFICATION_LISTENERS?=?"enabled_notification_listeners";??

  • ????private?static?final?String?ACTION_NOTIFICATION_LISTENER_SETTINGS?=?"android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS";??

  • ????private?boolean?isEnabledNLS?=?false;??

  • ????private?TextView?mTextView;??

  • ??

  • ????private?Handler?mHandler?=?new?Handler()?{??

  • ????????@Override??

  • ????????public?void?handleMessage(Message?msg)?{??

  • ????????????switch?(msg.what)?{??

  • ????????????????case?EVENT_SHOW_CREATE_NOS:??

  • ????????????//顯示創(chuàng)建的Notification對(duì)應(yīng)的pkgName、Tag、Id??

  • ????????????????????showCreateNotification();??

  • ????????????????????break;??

  • ????????????????case?EVENT_LIST_CURRENT_NOS:??

  • ????????????//顯示當(dāng)前所有的Notification數(shù)量及其包名??

  • ????????????????????listCurrentNotification();??

  • ????????????????????break;??

  • ??

  • ????????????????default:??

  • ????????????????????break;??

  • ????????????}??

  • ????????}??

  • ????};??

  • ??

  • ????@Override??

  • ????protected?void?onCreate(Bundle?savedInstanceState)?{??

  • ????????super.onCreate(savedInstanceState);??

  • ????????setContentView(R.layout.activity_main);??

  • ????????mTextView?=?(TextView)?findViewById(R.id.textView);??

  • ????}??

  • ??

  • ????@Override??

  • ????protected?void?onResume()?{??

  • ????????super.onResume();??

  • ????????//判斷是否有開(kāi)啟Notification?access??

  • ????????isEnabledNLS?=?isEnabled();??

  • ????????logNLS("isEnabledNLS?=?"?+?isEnabledNLS);??

  • ????????if?(!isEnabledNLS)?{??

  • ????????//如果沒(méi)有開(kāi)啟則顯示確認(rèn)對(duì)話(huà)框??

  • ????????????showConfirmDialog();??

  • ????????}??

  • ????}??

  • ??

  • ????public?void?buttonOnClicked(View?view)?{??

  • ????????mTextView.setTextColor(Color.BLACK);??

  • ????????switch?(view.getId())?{??

  • ????????????case?R.id.btnCreateNotify:??

  • ????????????????logNLS("Create?notifications...");??

  • ????????//創(chuàng)建可清除的Notification??

  • ????????????????createNotification(this);??

  • ????????//顯示當(dāng)前狀態(tài)欄中所有Notification數(shù)量及其包名??

  • ????????????????mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_SHOW_CREATE_NOS),?50);??

  • ????????????????break;??

  • ????????????case?R.id.btnClearLastNotify:??

  • ????????????????logNLS("Clear?Last?notification...");??

  • ????????//清除最后一個(gè)Notification??

  • ????????????????clearLastNotification();??

  • ????????//顯示當(dāng)前狀態(tài)欄中所有Notification數(shù)量及其包名??

  • ????????????????mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_LIST_CURRENT_NOS),?50);??

  • ????????????????break;??

  • ????????????case?R.id.btnClearAllNotify:??

  • ????????????????logNLS("Clear?All?notifications...");??

  • ????????//清除所有"可被清除"的Notification??

  • ????????????????clearAllNotifications();??

  • ????????????????mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_LIST_CURRENT_NOS),?50);??

  • ????????????????break;??

  • ????????????case?R.id.btnListNotify:??

  • ????????????????logNLS("List?notifications...");??

  • ????????????????listCurrentNotification();??

  • ????????????????break;??

  • ????????????case?R.id.btnEnableUnEnableNotify:??

  • ????????????????logNLS("Enable/UnEnable?notification...");??

  • ????????//打開(kāi)Notification?access啟動(dòng)/取消界面??

  • ????????????????openNotificationAccess();??

  • ????????????????break;??

  • ????????????default:??

  • ????????????????break;??

  • ????????}??

  • ????}??

  • ??

  • ????//......省略??

  • }??

  • 4. NotificationListenerService使用過(guò)程中有哪些注意事項(xiàng)?

    ? ? ? ??如果細(xì)心察看代碼的童鞋,一定發(fā)現(xiàn)代碼中有使用Handler,以及一些奇怪但又被注釋掉的代碼,比如"a.equals("b")"。從使用上來(lái)說(shuō),沒(méi)有必要使用handler,那干嘛要多次一舉?這里就給大家分享一下在寫(xiě)NotificationListenerDemo時(shí)遇到的一些坑。

    ①. NotificationMonitor的onCreate方法中使用handler來(lái)調(diào)用getActiveNotifications()方法

    ? ? ? ??若直接在onCreate或者onBind方法中調(diào)用getActiveNotifications()方法是無(wú)法獲取當(dāng)前系統(tǒng)通知。主要是因?yàn)镹otificationMonitor還未完成初始化,而根本原因則是INotificationListenerWrapper對(duì)象mWrapper還未初始化,此時(shí)使用getActiveNotifications()方法又會(huì)調(diào)用到mWrapper,因此無(wú)法返回正常數(shù)據(jù)。在NotificationListenerService中可以看到getActiveNotifications()的源碼:

    [java]?view plaincopy

  • public?StatusBarNotification[]?getActiveNotifications()?{??

  • ????try?{??

  • ????????return?getNotificationInterface().getActiveNotificationsFromListener(mWrapper);??

  • ????}?catch?(android.os.RemoteException?ex)?{??

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

  • ????}?????

  • ????return?null;??

  • }??

  • 也就是說(shuō)只要在onBind方法完成之后,再調(diào)用getActiveNotifications()方法就可以正常獲取數(shù)據(jù)了,因此這里使用了handler多線(xiàn)程的方式。當(dāng)然,為了保險(xiǎn)可以使用sendEmptyMessgeDelay加上延時(shí)。

    ②. 如果NotificationMonitor在onCreate或onBind方法中crash,則該service已經(jīng)失效,需重啟手機(jī)才能進(jìn)行后續(xù)開(kāi)發(fā)驗(yàn)證

    ? ? ? ??如果在onCreate或者onBind方法中,出現(xiàn)異常導(dǎo)致NotificationMonitor發(fā)生crash,就算找到問(wèn)題并將其改正,之后的驗(yàn)證還是無(wú)法繼續(xù)進(jìn)行的,也就是無(wú)法收到通知的新增和刪除消息,onNotificationPosted和onNotificationRemoved方法不會(huì)被調(diào)用。

    ? ? ? ? 這也是我在onBind方法中故意注釋導(dǎo)致空指針異常的代碼,有興趣的童鞋可以把注釋去掉后嘗試,去掉注釋會(huì)導(dǎo)致NotificationListenerDemo異常停止,此時(shí)你再加上注釋再次運(yùn)行NotificationListenerDemo,雖然程序可以正常啟動(dòng),但無(wú)法正常執(zhí)行NotificationMonitor中的onNotificationPosted和onNotificationRemoved方法。這個(gè)涉及NotificationListenerService的原理,后面會(huì)另行分析。

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

    ? ? ? ??當(dāng)點(diǎn)擊刪除通知時(shí),系統(tǒng)通知相關(guān)狀態(tài)還未更新,此時(shí)還沒(méi)有回調(diào)到NotificationMonitor中,所以獲取的數(shù)據(jù)就還是上一次的數(shù)據(jù)。為了能夠獲取到正確的Notification數(shù)據(jù),可以使用handler并加上延時(shí),這樣再去獲取Notification信息時(shí),系統(tǒng)已經(jīng)觸發(fā)了NotificationMonitor回調(diào),數(shù)據(jù)也有正常了。另外,50ms的延時(shí)幾乎是感知不到的。

    ④. 為什么要使用ArrayList來(lái)保存StatusBarNotification數(shù)組對(duì)象

    ? ? ? ??當(dāng)新增或者刪除通知時(shí),會(huì)觸發(fā)onNotificationPosted或onNotificationRemoved回調(diào),在該方法中調(diào)用getActiveNotifications()方法用以獲取當(dāng)前系統(tǒng)通知信息。而getActiveNotifications()返回的是StatusBarNotification[]數(shù)組,因?yàn)檫@個(gè)數(shù)組是可變長(zhǎng)的,也就是長(zhǎng)度會(huì)隨時(shí)變化,因此無(wú)法直接存儲(chǔ)。使用ArrayList可以很好的解決這個(gè)問(wèn)題,在ArrayList對(duì)象中添加一個(gè)StatusBarNotification[]對(duì)象,之后使用ArrayList.set(0,statusbar[])方法對(duì)數(shù)據(jù)進(jìn)行更新即可。

    總結(jié)

    ? ? ? ??NotificationListenerService是Android 4.3 之后新增的接口服務(wù),用于獲取系統(tǒng)Notification信息,這在之前的Android版本是無(wú)法直接辦到的。在Android 4.4中,增加了Notification.extra變量,使得獲取Notification相關(guān)信息更加豐富,這些接口的開(kāi)放更加利于三方應(yīng)用的使用,但同時(shí)也會(huì)帶來(lái)一些隱私問(wèn)題。

    ? ? ? ??本文針對(duì)NotificationListenerService的使用進(jìn)行了詳細(xì)分析,當(dāng)然其中不乏有失偏頗的地方,本著互聯(lián)網(wǎng)知識(shí)共享精神也將自己的一些記錄發(fā)布出來(lái),一來(lái)可做筆記,二來(lái)希望能夠給苦苦尋覓的童鞋一些幫助。

    ? ? ? ??后續(xù)會(huì)對(duì)NotificationListenerService的原理進(jìn)行分析,敬請(qǐng)期待。

    ? ? ? ??NotificationMonitor代碼免積分下載:下載Demo

    ? ? ? ??為了后續(xù)能夠更新,已經(jīng)代碼傳到github上,有興趣的童鞋可以在github上查看,連接戳這里。


    轉(zhuǎn)載于:https://my.oschina.net/tingzi/blog/413666

    總結(jié)

    以上是生活随笔為你收集整理的NotificationManagerService使用详解与原理分析(一)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。