Android 发送通知Framework处理流程

4 篇文章 1 订阅
3 篇文章 0 订阅

目录

1.时序图:

2.NotificationManger:

3.NotificationManagerService:

3.EnqueueNotificationRunnable:

5.PostNotificationRunnable的处理:

 6.NotificationListeners:


1.时序图:

从时序图上看执行流程很简单,接下来就一起跟随源码看看这些流程中都做了什么事情。

2.NotificationManger:

1):对smallicon的非空检验(Build.VERSION_CODES.LOLLIPOP_MR1以上版本),校验不通过报IllegalArgumentException("Invalid notification (no valid small icon),日志上有明显的提示,可以通过此条打印定位crash原因。

2)对LargeIcon压缩(若超过最大宽高MediaStyle(H:140dp W:280dp) BigPictureStyle(H:284dp W:416dp) 其他40dp),若未达到阀值则居中显示。

附上主要源码:

292    /**
293     * @hide
294     */
295    public void notifyAsUser(String tag, int id, Notification notification, UserHandle user)
296    {
297        INotificationManager service = getService();
298        String pkg = mContext.getPackageName();
299        // Fix the notification as best we can.
300        Notification.addFieldsFromContext(mContext, notification);
301        if (notification.sound != null) {
302            notification.sound = notification.sound.getCanonicalUri();
303            if (StrictMode.vmFileUriExposureEnabled()) {
304                notification.sound.checkFileUriExposed("Notification.sound");
305            }
306        }
307        fixLegacySmallIcon(notification, pkg);
308        if (mContext.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1) {
309            if (notification.getSmallIcon() == null) {
310                throw new IllegalArgumentException("Invalid notification (no valid small icon): "
311                        + notification);
312            }
313        }
314        if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")");
315        notification.reduceImageSizes(mContext);
316        ActivityManager am = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
317        boolean isLowRam = am.isLowRamDevice();
318        final Notification copy = Builder.maybeCloneStrippedForDelivery(notification, isLowRam);
319        try {
320            service.enqueueNotificationWithTag(pkg, mContext.getOpPackageName(), tag, id,
321                    copy, user.getIdentifier());
322        } catch (RemoteException e) {
323            throw e.rethrowFromSystemServer();
324        }
325    }

3.NotificationManagerService:

  • 检验Caller是否合法(主要是针对多用户的非管理用户的限制)
  • 参数是否合法(对nofiticaiton和packageName进行非空校验)
  • 是否能被渲染(猜测是沉浸式状态栏相关设置,只有系统APP才可以设置)
  • 保存通知状态(分为全局和根据Pakcagename区分的应用程序的)
  • 校验ChannelId是否合法(ChannelId是否成功注册了)
  • 封装Notification的相关信息到StatusBarNotification(实现Parcelable接口,用来给SystemUI传输数据)
  • 创建NotificationRecord,用来保存framework层私有数据(也就是不发送给SystemUI的数据)和StatusBarNotification。简单理解就是所有的通知数据;
  • 通知限制处理:

1):即时应用不允许发送通知;

2):通知的刷新频率不能太快,并且进度条通知的进度不能大于或等于最大值;

3):每个应用最多能同时存在50个通知;

  • 将通知的PenddingIntent加入到白名单中
  • 新建线程发送此条通知

附上主要源码:

3439    void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,
3440            final int callingPid, final String tag, final int id, final Notification notification,
3441            int incomingUserId) {
3442        if (DBG) {
3443            Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id
3444                    + " notification=" + notification);
3445        }
3446        checkCallerIsSystemOrSameApp(pkg);
3447
3448        final int userId = ActivityManager.handleIncomingUser(callingPid,
3449                callingUid, incomingUserId, true, false, "enqueueNotification", pkg);
3450        final UserHandle user = new UserHandle(userId);
3451
3452        if (pkg == null || notification == null) {
3453            throw new IllegalArgumentException("null not allowed: pkg=" + pkg
3454                    + " id=" + id + " notification=" + notification);
3455        }
3456
3457        // The system can post notifications for any package, let us resolve that.
3458        final int notificationUid = resolveNotificationUid(opPkg, callingUid, userId);
3459
3460        // Fix the notification as best we can.
3461        try {
3462            final ApplicationInfo ai = mPackageManagerClient.getApplicationInfoAsUser(
3463                    pkg, PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
3464                    (userId == UserHandle.USER_ALL) ? UserHandle.USER_SYSTEM : userId);
3465            Notification.addFieldsFromContext(ai, notification);
3466
3467            int canColorize = mPackageManagerClient.checkPermission(
3468                    android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, pkg);
3469            if (canColorize == PERMISSION_GRANTED) {
3470                notification.flags |= Notification.FLAG_CAN_COLORIZE;
3471            } else {
3472                notification.flags &= ~Notification.FLAG_CAN_COLORIZE;
3473            }
3474
3475        } catch (NameNotFoundException e) {
3476            Slog.e(TAG, "Cannot create a context for sending app", e);
3477            return;
3478        }
3479
3480        mUsageStats.registerEnqueuedByApp(pkg);
3481
3482        // setup local book-keeping
3483        String channelId = notification.getChannelId();
3484        if (mIsTelevision && (new Notification.TvExtender(notification)).getChannelId() != null) {
3485            channelId = (new Notification.TvExtender(notification)).getChannelId();
3486        }
3487        final NotificationChannel channel = mRankingHelper.getNotificationChannel(pkg,
3488                notificationUid, channelId, false /* includeDeleted */);
3489        if (channel == null) {
3490            final String noChannelStr = "No Channel found for "
3491                    + "pkg=" + pkg
3492                    + ", channelId=" + channelId
3493                    + ", id=" + id
3494                    + ", tag=" + tag
3495                    + ", opPkg=" + opPkg
3496                    + ", callingUid=" + callingUid
3497                    + ", userId=" + userId
3498                    + ", incomingUserId=" + incomingUserId
3499                    + ", notificationUid=" + notificationUid
3500                    + ", notification=" + notification;
3501            Log.e(TAG, noChannelStr);
3502            doChannelWarningToast("Developer warning for package \"" + pkg + "\"\n" +
3503                    "Failed to post notification on channel \"" + channelId + "\"\n" +
3504                    "See log for more details");
3505            return;
3506        }
3507
3508        final StatusBarNotification n = new StatusBarNotification(
3509                pkg, opPkg, id, tag, notificationUid, callingPid, notification,
3510                user, null, System.currentTimeMillis());
3511        final NotificationRecord r = new NotificationRecord(getContext(), n, channel);
3512
3513        if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0
3514                && (channel.getUserLockedFields() & NotificationChannel.USER_LOCKED_IMPORTANCE) == 0
3515                && (r.getImportance() == IMPORTANCE_MIN || r.getImportance() == IMPORTANCE_NONE)) {
3516            // Increase the importance of foreground service notifications unless the user had an
3517            // opinion otherwise
3518            if (TextUtils.isEmpty(channelId)
3519                    || NotificationChannel.DEFAULT_CHANNEL_ID.equals(channelId)) {
3520                r.setImportance(IMPORTANCE_LOW, "Bumped for foreground service");
3521            } else {
3522                channel.setImportance(IMPORTANCE_LOW);
3523                mRankingHelper.updateNotificationChannel(pkg, notificationUid, channel, false);
3524                r.updateNotificationChannel(channel);
3525            }
3526        }
3527
3528        if (!checkDisqualifyingFeatures(userId, notificationUid, id, tag, r,
3529                r.sbn.getOverrideGroupKey() != null)) {
3530            return;
3531        }
3532
3533        // Whitelist pending intents.
3534        if (notification.allPendingIntents != null) {
3535            final int intentCount = notification.allPendingIntents.size();
3536            if (intentCount > 0) {
3537                final ActivityManagerInternal am = LocalServices
3538                        .getService(ActivityManagerInternal.class);
3539                final long duration = LocalServices.getService(
3540                        DeviceIdleController.LocalService.class).getNotificationWhitelistDuration();
3541                for (int i = 0; i < intentCount; i++) {
3542                    PendingIntent pendingIntent = notification.allPendingIntents.valueAt(i);
3543                    if (pendingIntent != null) {
3544                        am.setPendingIntentWhitelistDuration(pendingIntent.getTarget(),
3545                                WHITELIST_TOKEN, duration);
3546                    }
3547                }
3548            }
3549        }
3550
3551        mHandler.post(new EnqueueNotificationRunnable(userId, r));
3552    }

3.EnqueueNotificationRunnable:

  • 加入mEnqueuedNotifications队列中
  • 定时取消功能
  • 对NotificationGroup的处理
  • 调用助理服务
  • 启动PostNotificationRunnable线程

主要源码如下:

3780    protected class EnqueueNotificationRunnable implements Runnable {
3781        private final NotificationRecord r;
3782        private final int userId;
3783
3784        EnqueueNotificationRunnable(int userId, NotificationRecord r) {
3785            this.userId = userId;
3786            this.r = r;
3787        };
3788
3789        @Override
3790        public void run() {
3791            synchronized (mNotificationLock) {
3792                mEnqueuedNotifications.add(r);
3793                scheduleTimeoutLocked(r);
3794
3795                final StatusBarNotification n = r.sbn;
3796                if (DBG) Slog.d(TAG, "EnqueueNotificationRunnable.run for: " + n.getKey());
3797                NotificationRecord old = mNotificationsByKey.get(n.getKey());
3798                if (old != null) {
3799                    // Retain ranking information from previous record
3800                    r.copyRankingInformation(old);
3801                }
3802
3803                final int callingUid = n.getUid();
3804                final int callingPid = n.getInitialPid();
3805                final Notification notification = n.getNotification();
3806                final String pkg = n.getPackageName();
3807                final int id = n.getId();
3808                final String tag = n.getTag();
3809
3810                // Handle grouped notifications and bail out early if we
3811                // can to avoid extracting signals.
3812                handleGroupedNotificationLocked(r, old, callingUid, callingPid);
3813
3814                // if this is a group child, unsnooze parent summary
3815                if (n.isGroup() && notification.isGroupChild()) {
3816                    mSnoozeHelper.repostGroupSummary(pkg, r.getUserId(), n.getGroupKey());
3817                }
3818
3819                // This conditional is a dirty hack to limit the logging done on
3820                //     behalf of the download manager without affecting other apps.
3821                if (!pkg.equals("com.android.providers.downloads")
3822                        || Log.isLoggable("DownloadManager", Log.VERBOSE)) {
3823                    int enqueueStatus = EVENTLOG_ENQUEUE_STATUS_NEW;
3824                    if (old != null) {
3825                        enqueueStatus = EVENTLOG_ENQUEUE_STATUS_UPDATE;
3826                    }
3827                    EventLogTags.writeNotificationEnqueue(callingUid, callingPid,
3828                            pkg, id, tag, userId, notification.toString(),
3829                            enqueueStatus);
3830                }
3831
3832                mRankingHelper.extractSignals(r);
3833
3834                // tell the assistant service about the notification
3835                if (mAssistants.isEnabled()) {
3836                    mAssistants.onNotificationEnqueued(r);
3837                    mHandler.postDelayed(new PostNotificationRunnable(r.getKey()),
3838                            DELAY_FOR_ASSISTANT_TIME);
3839                } else {
3840                    mHandler.post(new PostNotificationRunnable(r.getKey()));
3841                }
3842            }
3843        }
3844    }

5.PostNotificationRunnable的处理:

  • 判断通知新增还是刷新
  • 刷新NotificationGroup
  • 判断是否支持Zen Mode(勿扰模式)
  • 调用NotificationListeners的notifyPostedLocked方法

源码如下:

3846    protected class PostNotificationRunnable implements Runnable {
3847        private final String key;
3848
3849        PostNotificationRunnable(String key) {
3850            this.key = key;
3851        }
3852
3853        @Override
3854        public void run() {
3855            synchronized (mNotificationLock) {
3856                try {
3857                    NotificationRecord r = null;
3858                    int N = mEnqueuedNotifications.size();
3859                    for (int i = 0; i < N; i++) {
3860                        final NotificationRecord enqueued = mEnqueuedNotifications.get(i);
3861                        if (Objects.equals(key, enqueued.getKey())) {
3862                            r = enqueued;
3863                            break;
3864                        }
3865                    }
3866                    if (r == null) {
3867                        Slog.i(TAG, "Cannot find enqueued record for key: " + key);
3868                        return;
3869                    }
3870                    NotificationRecord old = mNotificationsByKey.get(key);
3871                    final StatusBarNotification n = r.sbn;
3872                    final Notification notification = n.getNotification();
3873                    int index = indexOfNotificationLocked(n.getKey());
3874                    if (index < 0) {
3875                        mNotificationList.add(r);
3876                        mUsageStats.registerPostedByApp(r);
3877                    } else {
3878                        old = mNotificationList.get(index);
3879                        mNotificationList.set(index, r);
3880                        mUsageStats.registerUpdatedByApp(r, old);
3881                        // Make sure we don't lose the foreground service state.
3882                        notification.flags |=
3883                                old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
3884                        r.isUpdate = true;
3885                    }
3886
3887                    mNotificationsByKey.put(n.getKey(), r);
3888
3889                    // Ensure if this is a foreground service that the proper additional
3890                    // flags are set.
3891                    if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
3892                        notification.flags |= Notification.FLAG_ONGOING_EVENT
3893                                | Notification.FLAG_NO_CLEAR;
3894                    }
3895
3896                    applyZenModeLocked(r);
3897                    mRankingHelper.sort(mNotificationList);
3898
3899                    if (notification.getSmallIcon() != null) {
3900                        StatusBarNotification oldSbn = (old != null) ? old.sbn : null;
3901                        mListeners.notifyPostedLocked(n, oldSbn);
3902                        if (oldSbn == null || !Objects.equals(oldSbn.getGroup(), n.getGroup())) {
3903                            mHandler.post(new Runnable() {
3904                                @Override
3905                                public void run() {
3906                                    mGroupHelper.onNotificationPosted(
3907                                            n, hasAutoGroupSummaryLocked(n));
3908                                }
3909                            });
3910                        }
3911                    } else {
3912                        Slog.e(TAG, "Not posting notification without small icon: " + notification);
3913                        if (old != null && !old.isCanceled) {
3914                            mListeners.notifyRemovedLocked(n,
3915                                    NotificationListenerService.REASON_ERROR);
3916                            mHandler.post(new Runnable() {
3917                                @Override
3918                                public void run() {
3919                                    mGroupHelper.onNotificationRemoved(n);
3920                                }
3921                            });
3922                        }
3923                        // ATTENTION: in a future release we will bail out here
3924                        // so that we do not play sounds, show lights, etc. for invalid
3925                        // notifications
3926                        Slog.e(TAG, "WARNING: In a future release this will crash the app: "
3927                                + n.getPackageName());
3928                    }
3929
3930                    buzzBeepBlinkLocked(r);//提示音
3931                } finally {
3932                    int N = mEnqueuedNotifications.size();
3933                    for (int i = 0; i < N; i++) {
3934                        final NotificationRecord enqueued = mEnqueuedNotifications.get(i);
3935                        if (Objects.equals(key, enqueued.getKey())) {
3936                            mEnqueuedNotifications.remove(i);
3937                            break;
3938                        }
3939                    }
3940                }
3941            }
3942        }
3943    }

 6.NotificationListeners:

  • 寻找匹配的ManagedService
  • 通知services移除不在显示的Notification
  • 向services发送新通知的信息
5534        /**
5535         * asynchronously notify all listeners about a new notification
5536         *
5537         * <p>
5538         * Also takes care of removing a notification that has been visible to a listener before,
5539         * but isn't anymore.
5540         */
5541        @GuardedBy("mNotificationLock")
5542        public void notifyPostedLocked(StatusBarNotification sbn, StatusBarNotification oldSbn) {
5543            // Lazily initialized snapshots of the notification.
5544            TrimCache trimCache = new TrimCache(sbn);
5545
5546            for (final ManagedServiceInfo info : getServices()) {
5547                boolean sbnVisible = isVisibleToListener(sbn, info);
5548                boolean oldSbnVisible = oldSbn != null ? isVisibleToListener(oldSbn, info) : false;
5549                // This notification hasn't been and still isn't visible -> ignore.
5550                if (!oldSbnVisible && !sbnVisible) {
5551                    continue;
5552                }
5553                final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
5554
5555                // This notification became invisible -> remove the old one.
5556                if (oldSbnVisible && !sbnVisible) {
5557                    final StatusBarNotification oldSbnLightClone = oldSbn.cloneLight();
5558                    mHandler.post(new Runnable() {
5559                        @Override
5560                        public void run() {
5561                            notifyRemoved(info, oldSbnLightClone, update, REASON_USER_STOPPED);
5562                        }
5563                    });
5564                    continue;
5565                }
5566
5567                final StatusBarNotification sbnToPost =  trimCache.ForListener(info);
5568                mHandler.post(new Runnable() {
5569                    @Override
5570                    public void run() {
5571                        notifyPosted(info, sbnToPost, update);
5572                    }
5573                });
5574            }
5575        }


5687        private void notifyPosted(final ManagedServiceInfo info,
5688                final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) {
5689            final INotificationListener listener = (INotificationListener) info.service;
5690            StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
5691            try {
5692                listener.onNotificationPosted(sbnHolder, rankingUpdate);
5693            } catch (RemoteException ex) {
5694                Log.e(TAG, "unable to notify listener (posted): " + listener, ex);
5695            }
5696        }

到此为止,通知的相关信息就发送到SystemUI中的Services中了,之后就是SystemUI的显示流程,下节在续

欢迎您来斧正。

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值