NotificationManagerService使用详解与原理分析(一)
概況
? ? ? ??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)題。
- 上一篇: 移动web开发规范
- 下一篇: 一键导出所有微信联系人的小工具,搞私域、