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
- @Override
- public 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() {
- @Override
- public 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;
- }
- //比较channelId
- if (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 again
- notificationList.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) {
- //找到通知后,把该通知从通知列表中清除,并把返回值设置为true
- mNotificationList.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;
- }
-
- // 处理通知lights
- NotificationRecord 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 repeatedly
- mNotificationLight.setFlashing(light.color, LogicalLight.LIGHT_FLASH_TIMED,
- light.onMs, light.offMs);
- }
- }
- }
至此,notificationManager.cancelAll()已分析完毕.