• Android 12.0 中 清除通知 , 系统源码分析(二)


    Android 提供了标准的api供第三方应用去清除通知,如下:

    1. NotificationManager notificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
    2.  
    3. notificationManager.cancel(id);//删除指定id的通知
    4. notificationManager.cancelAll();//删除全部通知


    针对的使用场景: 只能删除从该App中发出的通知,不能删除别的应用或者是系统的通知. 

    特别提示:notificationManager.cancelAll() 是删除由该APP发出的所有通知,即 "App的包名"对应下的所有通知.

    其中 "删除全部通知" 的源码分析如下: 

    (1)源码路径: frameworks/base/core/java/android/app/NotificationManager.java

    1. /**
    2. * 取消所有先前显示的通知.
    3. */
    4. public void cancelAll()
    5. {
    6. INotificationManager service = getService();
    7. //通过context获取应用的包名,即定义NotificationManager的应用的包名
    8. String pkg = mContext.getPackageName();
    9. if (localLOGV) Log.v(TAG, pkg + ": cancelAll()");
    10. try {
    11. //跨进程通信调用NotificationManagerService中的方法
    12. service.cancelAllNotifications(pkg, mContext.getUserId());
    13. } catch (RemoteException e) {
    14. throw e.rethrowFromSystemServer();
    15. }
    16. }

    (2) 源码路径: frameworks/base/services/core/java/com/android/server/notification/NotificationManagerService.java

    1. @Override
    2. public void cancelAllNotifications(String pkg, int userId) {
    3. checkCallerIsSystemOrSameApp(pkg);
    4. userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
    5. Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg);
    6. //不允许清除前提服务的通知,接着继续清除通知,
    7. cancelAllNotificationsInt(Binder.getCallingUid(), Binder.getCallingPid(),
    8. pkg, null, 0, FLAG_FOREGROUND_SERVICE, true, userId,
    9. REASON_APP_CANCEL_ALL, null);
    10. }
    11. //------------------------------------------------------
    12. /**
    13. * 清除给定包名的所有通知
    14. */
    15. void cancelAllNotificationsInt(int callingUid, int callingPid, String pkg, String channelId,
    16. int mustHaveFlags, int mustNotHaveFlags, boolean doit, int userId, int reason,
    17. ManagedServiceInfo listener) {
    18. mHandler.post(new Runnable() {
    19. @Override
    20. public void run() {
    21. String listenerName = listener == null ? null : listener.component.toShortString();
    22. EventLogTags.writeNotificationCancelAll(callingUid, callingPid,
    23. pkg, userId, mustHaveFlags, mustNotHaveFlags, reason,
    24. listenerName);
    25. // 设置退出参数
    26. if (!doit) {
    27. return;
    28. }
    29. synchronized (mNotificationLock) {
    30. FlagChecker flagChecker = (int flags) -> {
    31. if ((flags & mustHaveFlags) != mustHaveFlags) {
    32. return false;
    33. }
    34. if ((flags & mustNotHaveFlags) != 0) {
    35. return false;
    36. }
    37. return true;
    38. };
    39. //清除mNotificationList列表里保存的通知,看分析(3)
    40. cancelAllNotificationsByListLocked(mNotificationList, callingUid, callingPid,
    41. pkg, true /*nullPkgIndicatesUserSwitch*/, channelId, flagChecker,
    42. false /*includeCurrentProfiles*/, userId, false /*sendDelete*/, reason,
    43. listenerName, true /* wasPosted */);
    44. //清除mEnqueuedNotifications列表里保存的通知,看分析(3)
    45. cancelAllNotificationsByListLocked(mEnqueuedNotifications, callingUid,
    46. callingPid, pkg, true /*nullPkgIndicatesUserSwitch*/, channelId,
    47. flagChecker, false /*includeCurrentProfiles*/, userId,
    48. false /*sendDelete*/, reason, listenerName, false /* wasPosted */);
    49. mSnoozeHelper.cancel(userId, pkg);
    50. }
    51. }
    52. });
    53. }

    (3) 上面 注释的  清除mNotificationList 和清除 mEnqueuedNotifications 其实都时调用同一个方法,即cancelAllNotificationsByListLocked().区别在于传入的list不同,其中mNotificationList 保存了所有的通知, 而 mEnqueuedNotifications  则是保存了所有发送的到系统的通知,它们的定义如下:

    1. final ArrayList<NotificationRecord> mNotificationList = new ArrayList<>();
    2. final ArrayList<NotificationRecord> mEnqueuedNotifications = new ArrayList<>();
    3. final ArrayMap<String, NotificationRecord> mNotificationsByKey = new ArrayMap<>();

    接着分析 cancelAllNotificationsByListLocked()

    1. @GuardedBy("mNotificationLock")
    2. private void cancelAllNotificationsByListLocked(ArrayList<NotificationRecord> notificationList,
    3. int callingUid, int callingPid, String pkg, boolean nullPkgIndicatesUserSwitch,
    4. String channelId, FlagChecker flagChecker, boolean includeCurrentProfiles, int userId,
    5. boolean sendDelete, int reason, String listenerName, boolean wasPosted) {
    6. Set<String> childNotifications = null;
    7. //遍历notificationList,并根据给定的条件筛选
    8. for (int i = notificationList.size() - 1; i >= 0; --i) {
    9. NotificationRecord r = notificationList.get(i);
    10. if (includeCurrentProfiles) {
    11. if (!notificationMatchesCurrentProfiles(r, userId)) {
    12. continue;
    13. }
    14. } else if (!notificationMatchesUserId(r, userId)) {
    15. continue;
    16. }
    17. // 如果没有指定包名称,不能删除所有通知
    18. if (nullPkgIndicatesUserSwitch && pkg == null && r.getUserId() == UserHandle.USER_ALL) {
    19. continue;
    20. }
    21. if (!flagChecker.apply(r.getFlags())) {
    22. continue;
    23. }
    24. //比较包名
    25. if (pkg != null && !r.getSbn().getPackageName().equals(pkg)) {
    26. continue;
    27. }
    28. //比较channelId
    29. if (channelId != null && !channelId.equals(r.getChannel().getId())) {
    30. continue;
    31. }
    32. if (r.getSbn().isGroup() && r.getNotification().isGroupChild()) {
    33. if (childNotifications == null) {
    34. childNotifications = new HashSet<>();
    35. }
    36. childNotifications.add(r.getKey());
    37. continue;
    38. }
    39. //从notificationList中清除通知
    40. notificationList.remove(i);
    41. //从mNotificationsByKey中清除通知, mNotificationsByKey是保存了通知Key和通知的对应关系,
    42. mNotificationsByKey.remove(r.getKey());
    43. r.recordDismissalSentiment(NotificationStats.DISMISS_SENTIMENT_NEUTRAL);
    44. //把通知从列表中清除后,还需要对通知的资源进行回收处理,分析(4)
    45. cancelNotificationLocked(r, sendDelete, reason, wasPosted, listenerName);
    46. }
    47. if (childNotifications != null) {
    48. final int M = notificationList.size();
    49. for (int i = M - 1; i >= 0; i--) {
    50. NotificationRecord r = notificationList.get(i);
    51. if (childNotifications.contains(r.getKey())) {
    52. // dismiss conditions were checked in the first loop and so don't need to be
    53. // checked again
    54. notificationList.remove(i);
    55. mNotificationsByKey.remove(r.getKey());
    56. r.recordDismissalSentiment(NotificationStats.DISMISS_SENTIMENT_NEUTRAL);
    57. cancelNotificationLocked(r, sendDelete, reason, wasPosted, listenerName);
    58. }
    59. }
    60. //更新通知设定的LED,分析(5)
    61. updateLightsLocked();
    62. }
    63. }

    (4)继续分析 removeFromNotificationListsLocked() ,通知的资源释放

    1. @GuardedBy("mNotificationLock")
    2. private boolean removeFromNotificationListsLocked(NotificationRecord r) {
    3. // 从两个列表中删除,任一列表都可以有一个单独的记录,实际上是相同的通知。
    4. boolean wasPosted = false;
    5. //NotificationRecord在NotificationManagerService中代表一个通知,
    6. NotificationRecord recordInList = null;
    7. //根据指定的通知的key,在通知列表中查询是否有符合的通知
    8. if ((recordInList = findNotificationByListLocked(mNotificationList, r.getKey()))
    9. != null) {
    10. //找到通知后,把该通知从通知列表中清除,并把返回值设置为true
    11. mNotificationList.remove(recordInList);
    12. mNotificationsByKey.remove(recordInList.getSbn().getKey());
    13. wasPosted = true;
    14. }
    15. while ((recordInList = findNotificationByListLocked(mEnqueuedNotifications, r.getKey()))
    16. != null) {
    17. //mEnqueuedNotifications 是一个list,里面保存了所有发送的通知,如果在该list中也存在需要删除的通知,则把该通知也从list中清除
    18. mEnqueuedNotifications.remove(recordInList);
    19. }
    20. return wasPosted;
    21. }

    (5) 最后 处理通知的LED, updateLightsLocked()

    1. @GuardedBy("mNotificationLock")
    2. void updateLightsLocked()
    3. {
    4. if (mNotificationLight == null) {
    5. return;
    6. }
    7. // 处理通知lights
    8. NotificationRecord ledNotification = null;
    9. while (ledNotification == null && !mLights.isEmpty()) {
    10. final String owner = mLights.get(mLights.size() - 1);
    11. ledNotification = mNotificationsByKey.get(owner);
    12. if (ledNotification == null) {
    13. Slog.wtfStack(TAG, "LED Notification does not exist: " + owner);
    14. mLights.remove(owner);
    15. }
    16. }
    17. // 通话或屏幕打开时不要闪烁
    18. if (ledNotification == null || isInCall() || mScreenOn) {
    19. mNotificationLight.turnOff();
    20. } else {
    21. NotificationRecord.Light light = ledNotification.getLight();
    22. if (light != null && mNotificationPulseEnabled) {
    23. // pulse repeatedly
    24. mNotificationLight.setFlashing(light.color, LogicalLight.LIGHT_FLASH_TIMED,
    25. light.onMs, light.offMs);
    26. }
    27. }
    28. }

    至此,notificationManager.cancelAll()已分析完毕.

  • 相关阅读:
    迎合国家新政策,共享购联合共享经济,三方互利,消费增值
    【Vue】基础语法(创建项目|数据绑定|事件绑定|声明方法|插值表达式|属性值绑定|循环数组|v-if控制||计算属性|监听器|过滤器)
    shell脚本常用语句记录--持续更新
    QT常用的控件总结
    (web前端网页制作课作业)使用HTML+CSS制作非物质文化遗产专题网页设计与实现
    学习经验分享之十二:如何撰写论文
    QT-地形3D
    携程apollo配置中心服务端如何感知配置更新?
    Dubbo Gateway - 网关设计
    [carla入门教程]-4 carla中的地图(附:鸟瞰图和道路图)
  • 原文地址:https://blog.csdn.net/Smile_729day/article/details/134404455