Android 提供了标准的api供第三方应用去清除通知,如下:
NotificationManager notificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);notificationManager.cancel(id);//删除指定id的通知
notificationManager.cancelAll();//删除全部通知
针对的使用场景: 只能删除从该App中发出的通知,不能删除别的应用或者是系统的通知.
特别提示:notificationManager.cancelAll() 是删除由该APP发出的所有通知,即 "App的包名"对应下的所有通知.
其中 "删除全部通知" 的源码分析如下:
(1)源码路径: frameworks/base/core/java/android/app/NotificationManager.java
/*** 取消所有先前显示的通知. */public void cancelAll(){INotificationManager service = getService();//通过context获取应用的包名,即定义NotificationManager的应用的包名String pkg = mContext.getPackageName();if (localLOGV) Log.v(TAG, pkg + ": cancelAll()");try {//跨进程通信调用NotificationManagerService中的方法service.cancelAllNotifications(pkg, mContext.getUserId());} catch (RemoteException e) {throw e.rethrowFromSystemServer();}}
(2) 源码路径: frameworks/base/services/core/java/com/android/server/notification/NotificationManagerService.java
@Overridepublic void cancelAllNotifications(String pkg, int userId) {checkCallerIsSystemOrSameApp(pkg);userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg);//不允许清除前提服务的通知,接着继续清除通知,cancelAllNotificationsInt(Binder.getCallingUid(), Binder.getCallingPid(),pkg, null, 0, FLAG_FOREGROUND_SERVICE, true, userId,REASON_APP_CANCEL_ALL, null);}//------------------------------------------------------/*** 清除给定包名的所有通知*/void cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, String channelId,int mustHaveFlags, int mustNotHaveFlags, boolean doit, int userId, int reason,ManagedServiceInfo listener) {mHandler.post(new Runnable() {@Overridepublic void run() {String listenerName = listener == null ? null : listener.component.toShortString();EventLogTags.writeNotificationCancelAll(callingUid, callingPid,pkg, userId, mustHaveFlags, mustNotHaveFlags, reason,listenerName);// 设置退出参数if (!doit) {return;}synchronized (mNotificationLock) {FlagChecker flagChecker = (int flags) -> {if ((flags & mustHaveFlags) != mustHaveFlags) {return false;}if ((flags & mustNotHaveFlags) != 0) {return false;}return true;};//清除mNotificationList列表里保存的通知,看分析(3)cancelAllNotificationsByListLocked(mNotificationList, callingUid, callingPid,pkg, true /*nullPkgIndicatesUserSwitch*/, channelId, flagChecker,false /*includeCurrentProfiles*/, userId, false /*sendDelete*/, reason,listenerName, true /* wasPosted */);//清除mEnqueuedNotifications列表里保存的通知,看分析(3)cancelAllNotificationsByListLocked(mEnqueuedNotifications, callingUid,callingPid, pkg, true /*nullPkgIndicatesUserSwitch*/, channelId,flagChecker, false /*includeCurrentProfiles*/, userId,false /*sendDelete*/, reason, listenerName, false /* wasPosted */);mSnoozeHelper.cancel(userId, pkg);}}});}
(3) 上面 注释的 清除mNotificationList 和清除 mEnqueuedNotifications 其实都时调用同一个方法,即cancelAllNotificationsByListLocked().区别在于传入的list不同,其中mNotificationList 保存了所有的通知, 而 mEnqueuedNotifications 则是保存了所有发送的到系统的通知,它们的定义如下:
final ArrayList<NotificationRecord> mNotificationList = new ArrayList<>();final ArrayList<NotificationRecord> mEnqueuedNotifications = new ArrayList<>();final ArrayMap<String, NotificationRecord> mNotificationsByKey = new ArrayMap<>();
接着分析 cancelAllNotificationsByListLocked()
@GuardedBy("mNotificationLock")private void cancelAllNotificationsByListLocked(ArrayList<NotificationRecord> notificationList,int callingUid, int callingPid, String pkg, boolean nullPkgIndicatesUserSwitch,String channelId, FlagChecker flagChecker, boolean includeCurrentProfiles, int userId,boolean sendDelete, int reason, String listenerName, boolean wasPosted) {Set<String> childNotifications = null;//遍历notificationList,并根据给定的条件筛选for (int i = notificationList.size() - 1; i >= 0; --i) {NotificationRecord r = notificationList.get(i);if (includeCurrentProfiles) {if (!notificationMatchesCurrentProfiles(r, userId)) {continue;}} else if (!notificationMatchesUserId(r, userId)) {continue;}// 如果没有指定包名称,不能删除所有通知if (nullPkgIndicatesUserSwitch && pkg == null && r.getUserId() == UserHandle.USER_ALL) {continue;}if (!flagChecker.apply(r.getFlags())) {continue;}//比较包名if (pkg != null && !r.getSbn().getPackageName().equals(pkg)) {continue;}//比较channelIdif (channelId != null && !channelId.equals(r.getChannel().getId())) {continue;}if (r.getSbn().isGroup() && r.getNotification().isGroupChild()) {if (childNotifications == null) {childNotifications = new HashSet<>();}childNotifications.add(r.getKey());continue;}//从notificationList中清除通知notificationList.remove(i);//从mNotificationsByKey中清除通知, mNotificationsByKey是保存了通知Key和通知的对应关系,mNotificationsByKey.remove(r.getKey());r.recordDismissalSentiment(NotificationStats.DISMISS_SENTIMENT_NEUTRAL);//把通知从列表中清除后,还需要对通知的资源进行回收处理,分析(4)cancelNotificationLocked(r, sendDelete, reason, wasPosted, listenerName);}if (childNotifications != null) {final int M = notificationList.size();for (int i = M - 1; i >= 0; i--) {NotificationRecord r = notificationList.get(i);if (childNotifications.contains(r.getKey())) {// dismiss conditions were checked in the first loop and so don't need to be// checked againnotificationList.remove(i);mNotificationsByKey.remove(r.getKey());r.recordDismissalSentiment(NotificationStats.DISMISS_SENTIMENT_NEUTRAL);cancelNotificationLocked(r, sendDelete, reason, wasPosted, listenerName);}}//更新通知设定的LED,分析(5)updateLightsLocked();}}
(4)继续分析 removeFromNotificationListsLocked() ,通知的资源释放
@GuardedBy("mNotificationLock")private boolean removeFromNotificationListsLocked(NotificationRecord r) {// 从两个列表中删除,任一列表都可以有一个单独的记录,实际上是相同的通知。boolean wasPosted = false;//NotificationRecord在NotificationManagerService中代表一个通知,NotificationRecord recordInList = null;//根据指定的通知的key,在通知列表中查询是否有符合的通知if ((recordInList = findNotificationByListLocked(mNotificationList, r.getKey()))!= null) {//找到通知后,把该通知从通知列表中清除,并把返回值设置为truemNotificationList.remove(recordInList);mNotificationsByKey.remove(recordInList.getSbn().getKey());wasPosted = true;}while ((recordInList = findNotificationByListLocked(mEnqueuedNotifications, r.getKey()))!= null) {//mEnqueuedNotifications 是一个list,里面保存了所有发送的通知,如果在该list中也存在需要删除的通知,则把该通知也从list中清除mEnqueuedNotifications.remove(recordInList);}return wasPosted;}
(5) 最后 处理通知的LED, updateLightsLocked()
@GuardedBy("mNotificationLock")void updateLightsLocked(){if (mNotificationLight == null) {return;}// 处理通知lightsNotificationRecord ledNotification = null;while (ledNotification == null && !mLights.isEmpty()) {final String owner = mLights.get(mLights.size() - 1);ledNotification = mNotificationsByKey.get(owner);if (ledNotification == null) {Slog.wtfStack(TAG, "LED Notification does not exist: " + owner);mLights.remove(owner);}}// 通话或屏幕打开时不要闪烁if (ledNotification == null || isInCall() || mScreenOn) {mNotificationLight.turnOff();} else {NotificationRecord.Light light = ledNotification.getLight();if (light != null && mNotificationPulseEnabled) {// pulse repeatedlymNotificationLight.setFlashing(light.color, LogicalLight.LIGHT_FLASH_TIMED,light.onMs, light.offMs);}}}
至此,notificationManager.cancelAll()已分析完毕.