源码剖析: Notification的发送

NotificationManagerService启动

system_server进程中,启动了NotificationManagerService

frameworks/base/services/java/com/android/server/SystemServer.java

private void startOtherServices() {
    mSystemServiceManager.startService(NotificationManagerService.class);
}

看下SystemServiceManager如何启动它的

frameworks/base/services/core/java/com/android/server/SystemServiceManager.java

    public <T extends SystemService> T startService(Class<T> serviceClass) {
        try {
            final String name = serviceClass.getName();
            
			// ...
			
            final T service;
            try {
                Constructor<T> constructor = serviceClass.getConstructor(Context.class);
                service = constructor.newInstance(mContext);
            } catch (Exception ex) {
            }
			
			// ...
			
            startService(service);
            return service;
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
        }
    }
    public void startService(@NonNull final SystemService service) {
        // Register it.
        mServices.add(service);
        // Start it.
        long time = SystemClock.elapsedRealtime();
        try {
            service.onStart();
        } catch (RuntimeException ex) {
            throw new RuntimeException("Failed to start service " + service.getClass().getName()
                    + ": onStart threw an exception", ex);
        }
        warnIfTooLong(SystemClock.elapsedRealtime() - time, service, "onStart");
    }

通过反射获取带Context参数的构造函数,然后创建对象,最后调用对象的onStart()方法。

NotificationManagerService的构造函数很简单,就是保存Context参数,而它的onStart()方法进行大量的变量初始化,当我们以后需要这些变量的时候再来分析它们初始化过程。

注册NotificationChannel

从Android 8.0开始,在发送通知前,需要向系统注册通知通道(Notification Channel),注册通道的示例代码如下。

private void createNotificationChannel() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        CharSequence name = getString(R.string.channel_name);
        String description = getString(R.string.channel_description);
        int importance = NotificationManager.IMPORTANCE_DEFAULT;
        // 创建通道
        NotificationChannel channel = new NotificationChannel(CHANNEL_ID, name, importance);
        channel.setDescription(description);
        NotificationManager notificationManager = getSystemService(NotificationManager.class);
        // 注册通道
        notificationManager.createNotificationChannel(channel);
    }
}

先看下如何构造NotificationChannel

    public NotificationChannel(String id, CharSequence name, @Importance int importance) {
        this.mId = getTrimmedString(id);
        this.mName = name != null ? getTrimmedString(name.toString()) : null;
        this.mImportance = importance;
    }
    
    private String getTrimmedString(String input) {
        if (input != null && input.length() > MAX_TEXT_LENGTH) {
            return input.substring(0, MAX_TEXT_LENGTH);
        }
        return input;
    }
    
    public void setDescription(String description) {
        mDesc = getTrimmedString(description);
    }

构造函数很简单,就是简单的变量的赋值。当我们在手机的Settings中查看一个应用的通知设置的时候,如果这个应用向系统注册过通知通道,那么你就会看到各种通道,每一个通道会显示一个name和一个description

通知通道创建完毕后,然后通过NotificationManager向系统注册

    /**
     * Creates a notification channel that notifications can be posted to.
     *
     * This can also be used to restore a deleted channel and to update an existing channel's
     * name, description, and/or importance.
     *
     * <p>The name and description should only be changed if the locale changes
     * or in response to the user renaming this channel. For example, if a user has a channel
     * named 'John Doe' that represents messages from a 'John Doe', and 'John Doe' changes his name
     * to 'John Smith,' the channel can be renamed to match.
     *
     * <p>The importance of an existing channel will only be changed if the new importance is lower
     * than the current value and the user has not altered any settings on this channel.
     *
     * All other fields are ignored for channels that already exist.
     */
    public void createNotificationChannel(@NonNull NotificationChannel channel) {
        createNotificationChannels(Arrays.asList(channel));
    }
    
    public void createNotificationChannels(@NonNull List<NotificationChannel> channels) {
        INotificationManager service = getService();
        try {
            service.createNotificationChannels(mContext.getPackageName(),
                    new ParceledListSlice(channels));
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

NotificationManager最终会把信息发送给服务端,服务端的实现在NotificationManagerService.java

public class NotificationManagerService extends SystemService {
     private final IBinder mService = new INotificationManager.Stub() {
        @Override
        public void createNotificationChannels(String pkg,
                ParceledListSlice channelsList) throws RemoteException {
            // 检查权限,system进程,phone进程,root进程以及当前app有权限
            checkCallerIsSystemOrSameApp(pkg);
            createNotificationChannelsImpl(pkg, Binder.getCallingUid(), channelsList);
        }

        private void createNotificationChannelsImpl(String pkg, int uid,
                ParceledListSlice channelsList) {
            List<NotificationChannel> channels = channelsList.getList();
            final int channelsSize = channels.size();
            // 遍历并注册通道
            for (int i = 0; i < channelsSize; i++) {
                final NotificationChannel channel = channels.get(i);
                // 注册的通道不能为null
                Preconditions.checkNotNull(channel, "channel in list is null");
                // 创建通道
                mRankingHelper.createNotificationChannel(pkg, uid, channel,
                        true /* fromTargetApp */);
                // 通知监听者通道已经改变(监听者包括状态栏)
                mListeners.notifyNotificationChannelChanged(pkg,
                        UserHandle.getUserHandleForUid(uid),
                        mRankingHelper.getNotificationChannel(pkg, uid, channel.getId(), false),
                        NOTIFICATION_CHANNEL_OR_GROUP_ADDED);
            }
            savePolicyFile();
        }
     }
 }

通道的通过mRankingHelper来创建的,前面介绍过这个变量的创建过程,现在直接看下它如何创建通道的

public class RankingHelper implements RankingConfig {

    public void createNotificationChannel(String pkg, int uid, NotificationChannel channel,
            boolean fromTargetApp) {
		// ...
			
		// 1. 获取或者创建Record对象
        Record r = getOrCreateRecord(pkg, uid);
        if (r == null) {
            throw new IllegalArgumentException("Invalid package");
        }
        if (channel.getGroup() != null && !r.groups.containsKey(channel.getGroup())) {
            throw new IllegalArgumentException("NotificationChannelGroup doesn't exist");
        }
        if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(channel.getId())) {
            throw new IllegalArgumentException("Reserved id");
        }

        NotificationChannel existing = r.channels.get(channel.getId());
        
        // 如果通道已经存在就更新通道
        if (existing != null && fromTargetApp) {
			// ...
        }
        // 通道的importance属性的范围检测
        if (channel.getImportance() < NotificationManager.IMPORTANCE_NONE
                || channel.getImportance() > NotificationManager.IMPORTANCE_MAX) {
            throw new IllegalArgumentException("Invalid importance level");
        }
        
        // 2. 根据创建的Record对象更新channel的属性(这里覆盖了外部API调用给channel设置的这些属性)
        if (fromTargetApp) {
        	// false
            channel.setBypassDnd(r.priority == Notification.PRIORITY_MAX);
            // r.visibility值为NotificationManager.VISIBILITY_NO_OVERRIDE
            channel.setLockscreenVisibility(r.visibility);
        }
        clearLockedFields(channel);
        if (channel.getLockscreenVisibility() == Notification.VISIBILITY_PUBLIC) {
            channel.setLockscreenVisibility(Ranking.VISIBILITY_NO_OVERRIDE);
        }
        // r.showBadge值为true
        if (!r.showBadge) {
            channel.setShowBadge(false);
        }
        
        // 3. 用创建的Record对象保存channel
        r.channels.put(channel.getId(), channel);
        MetricsLogger.action(getChannelLog(channel, pkg).setType(
                MetricsProto.MetricsEvent.TYPE_OPEN));
    }
}

创建通道的过程大致分为三步,首先看第一步,利用包名和用户ID创建Record对象

    private static final int DEFAULT_PRIORITY = Notification.PRIORITY_DEFAULT;
    private static final int DEFAULT_VISIBILITY = NotificationManager.VISIBILITY_NO_OVERRIDE;
    private static final int DEFAULT_IMPORTANCE = NotificationManager.IMPORTANCE_UNSPECIFIED;
    private static final boolean DEFAULT_SHOW_BADGE = true;

    private Record getOrCreateRecord(String pkg, int uid) {
        return getOrCreateRecord(pkg, uid,
                DEFAULT_IMPORTANCE, DEFAULT_PRIORITY, DEFAULT_VISIBILITY, DEFAULT_SHOW_BADGE);
    }
    private Record getOrCreateRecord(String pkg, int uid, int importance, int priority,
            int visibility, boolean showBadge) {
        // 生成唯一的key值,形式为pkg + "|" + uid
        final String key = recordKey(pkg, uid);
        synchronized (mRecords) {
       		// 从缓存中获取Record对象
            Record r = (uid == Record.UNKNOWN_UID) ? mRestoredWithoutUids.get(pkg) : mRecords.get(
                    key);
            // 如果是首次注册通道,r为null        
            if (r == null) {
            	// 创建Record对象,并给它的变量赋值
                r = new Record();
                r.pkg = pkg;
                r.uid = uid;
                r.importance = importance;
                r.priority = priority;
                r.visibility = visibility;
                r.showBadge = showBadge;

                try {
                	// 这里是为了兼容版本处理,如果是Android 8.0以上的版本,这里不做任何处理
                    createDefaultChannelIfNeeded(r);
                } catch (NameNotFoundException e) {
                    Slog.e(TAG, "createDefaultChannelIfNeeded - Exception: " + e);
                }
				
				// 把创建的Record对象保存到mRecords中
                if (r.uid == Record.UNKNOWN_UID) {
                    mRestoredWithoutUids.put(pkg, r);
                } else {
                    mRecords.put(key, r);
                }
            }
            return r;
        }
    }

对于首次注册通道的应用,需要创建Record对象,然后保存到mRecords中,我们可以注意到,对Record变量的某些属性进行赋值的时候使用了一些默认的值,例如importance , priority属性等等。

有了Record对象后,现在就到了createNotificationChannel()的第二步,利用这个Record对象更新通道的属性,这里可以对比创建Record对象使用的默认属性来分析,然后我们需要注意的是,这些属性其实都可以利用API设置,但是这里显然是覆盖了这些属性。然后就是第三步,用创建的Record对象保存了channel

至此,通知通道的注册已经分析完毕,就是保存数据,大致步骤如下

  1. 为每个应用创建一个Record对象,并保存到mRecords
  2. 用这个创建出来的Record对象更新NotificationChannel的某些属性,例如showBagelockscreenVisibility等等。
  3. 最后用这个创建的Record对象保存NotificationChannel

发送通知

通知创建完毕后,就可以向通道发送通知了,示例代码如下。

    private void showNotification() {
        Notification notification = new Notification.Builder(this, CHANNEL_ID)
                .setSmallIcon(R.mipmap.ic_launcher_round)
                .setContentTitle("My notification")
                .setContentText("Hello world!")
                .build();

        NotificationManager notificationManager = getSystemService(Context.NOTIFICATION);
        notificationManager.notify(110, notification);
    }

从Android 8.0开始,在构建Notification对象的时候,必须要传入通知通道的ID,这点可以从上面代码的Notification.Builder的构造函数中发现。

我们先来看下如何构建Notification对象,这里使用的是Builder模式

public class Notification implements Parcelable{
	private Notification mN;
	
    public static class Builder {
        public Builder(Context context, String channelId) {
            this(context, (Notification) null);
            mN.mChannelId = channelId;
        }

        public Builder(Context context, Notification toAdopt) {
            mContext = context;
            Resources res = mContext.getResources();
            // true	
            mTintActionButtons = res.getBoolean(R.bool.config_tintNotificationActionButtons);

			// false
            if (res.getBoolean(R.bool.config_enableNightMode)) {
                Configuration currentConfig = res.getConfiguration();
                mInNightMode = (currentConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK)
                        == Configuration.UI_MODE_NIGHT_YES;
            }

			// 传入的toAdopt参数为null
            if (toAdopt == null) {
                mN = new Notification();
                if (context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) {
                    mN.extras.putBoolean(EXTRA_SHOW_WHEN, true);
                }
                // 0
                mN.priority = PRIORITY_DEFAULT;
                // 0,代表在锁屏界面隐藏隐私信息
                mN.visibility = VISIBILITY_PRIVATE;
            } else {
				// ...
            }
        }
    }
}

Notification.Builder的对象的构造非常简单,只是给mN变量赋值,并设置了一些参数。

那么现在看下给这个Builder对象设置其他一些参数,示例代码中看到设置了三个参数,一个是小图标,一个是标题,一个是内容。先看下标题和内容,这部分比较简单。

        public Builder setContentTitle(CharSequence title) {
            mN.extras.putCharSequence(EXTRA_TITLE, safeCharSequence(title));
            return this;
        }

        public Builder setContentText(CharSequence text) {
            mN.extras.putCharSequence(EXTRA_TEXT, safeCharSequence(text));
            return this;
        }  

很简单,就是用mN.extras这个Bundle对象保存数据。

再来看下如何保存小图标的

        public Builder setSmallIcon(@DrawableRes int icon) {
            return setSmallIcon(icon != 0
                    ? Icon.createWithResource(mContext, icon)
                    : null);
        }
        public Builder setSmallIcon(Icon icon) {
            mN.setSmallIcon(icon);
            if (icon != null && icon.getType() == Icon.TYPE_RESOURCE) {
                mN.icon = icon.getResId();
            }
            return this;
        }

设置小图标有多种方法,这里分析代码中所展示的情况。可以看到也是用mN来保存的,只是把资源ID转化为一个Icon对象保存,可以看下如何转化的

public final class Icon implements Parcelable {
    private Icon(int mType) {
        this.mType = mType;
    }
    
    /**
     * Create an Icon pointing to a drawable resource.
     */
    public static Icon createWithResource(Context context, @DrawableRes int resId) {
        if (context == null) {
            throw new IllegalArgumentException("Context must not be null.");
        }
        final Icon rep = new Icon(TYPE_RESOURCE);
        rep.mInt1 = resId;
        rep.mString1 = context.getPackageName();
        return rep;
    }
}    

非常简单,就是一个加上包名和资源类型名的一个封装。

Notificatin.Builder模式已经构建完毕,现在就是利用这个Builder对象创建Notification对象了

public class Notification implements Parcelable
    public static class Builder {
        public Notification build() {
            // 示例代码中没有设置过,略过
            if (mUserExtras != null) {
                mN.extras = getAllExtras();
            }

            mN.creationTime = System.currentTimeMillis();

            // 其实就是mN.extras.putParcelable(EXTRA_BUILDER_APPLICATION_INFO, applicationInfo)
            // 保存ApplicationInfo信息
            Notification.addFieldsFromContext(mContext, mN);

			// 示例代码中没有设置过,略过
            buildUnstyled();
			
			// 没有设置过Style
            if (mStyle != null) {
                mStyle.reduceImageSizes(mContext);
                mStyle.purgeResources();
                mStyle.buildStyled(mN);
            }
            // 对大图进行压缩,代码中没有设置过大图,略过
            mN.reduceImageSizes(mContext);

			// 小于N的版本的处理,略过
            if (mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N
                    && (useExistingRemoteView())) {
				// ...
            }

			// 目前还没有设置过DEFAULT_LIGHTS这个标志
            if ((mN.defaults & DEFAULT_LIGHTS) != 0) {
                mN.flags |= FLAG_SHOW_LIGHTS;
            }

            return mN;
        }
    }
}

这里其实也是对Notification.Builder中设置的参数保存到mN变量中,为了方便分析,我们假设处理的情况是大于Android N版本的。

现在Notification对象已经创建完毕,然后使用NotificationManager发送这个通知

public class NotificationManager {
    /**
     * Post a notification to be shown in the status bar. If a notification with
     * the same id has already been posted by your application and has not yet been canceled, it
     * will be replaced by the updated information.
     */
    public void notify(int id, Notification notification)
    {
        notify(null, id, notification);
    }

    public void notify(String tag, int id, Notification notification)
    {
        notifyAsUser(tag, id, notification, new UserHandle(UserHandle.myUserId()));
    }

    public void notifyAsUser(String tag, int id, Notification notification, UserHandle user)
    {
        INotificationManager service = getService();
        String pkg = mContext.getPackageName();
        // 前面代码中已经保存过参数
        Notification.addFieldsFromContext(mContext, notification);
        // 处理设置过铃声的情况
        if (notification.sound != null) {
            notification.sound = notification.sound.getCanonicalUri();
            if (StrictMode.vmFileUriExposureEnabled()) {
                notification.sound.checkFileUriExposed("Notification.sound");
            }
        }
        // 修复历史遗留的小图标问题
        fixLegacySmallIcon(notification, pkg);
		
		// 5.0之后,小图标不能为null
        if (mContext.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1) {
            if (notification.getSmallIcon() == null) {
                throw new IllegalArgumentException("Invalid notification (no valid small icon): "
                        + notification);
            }
        }
        if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")");
        // 压缩大图,前面已经做过
        notification.reduceImageSizes(mContext);
        ActivityManager am = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
        boolean isLowRam = am.isLowRamDevice();

		// 根据手机配置是否是low ram,去掉一些参数,按照示例代码分析,这里不做任何事情
        final Notification copy = Builder.maybeCloneStrippedForDelivery(notification, isLowRam);
        try {
        	// 向服务端发送通知
            service.enqueueNotificationWithTag(pkg, mContext.getOpPackageName(), tag, id,
                    copy, user.getIdentifier());
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }	
}

在对Notification对象做一些处理后,向服务端发送信息,服务端的实现在NotificationManagerService.java

接收通知

public class NotificationManagerService extends SystemService {
    private final IBinder mService = new INotificationManager.Stub() {
        public void enqueueNotificationWithTag(String pkg, String opPkg, String tag, int id,
                Notification notification, int userId) throws RemoteException {
            enqueueNotificationInternal(pkg, opPkg, Binder.getCallingUid(),
                    Binder.getCallingPid(), tag, id, notification, userId);
        }

    void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,
            final int callingPid, final String tag, final int id, final Notification notification,
            int incomingUserId) {
		// 检查权限
        checkCallerIsSystemOrSameApp(pkg);

        final int userId = ActivityManager.handleIncomingUser(callingPid,
                callingUid, incomingUserId, true, false, "enqueueNotification", pkg);
        final UserHandle user = new UserHandle(userId);

        if (pkg == null || notification == null) {
            throw new IllegalArgumentException("null not allowed: pkg=" + pkg
                    + " id=" + id + " notification=" + notification);
        }

        // The system can post notifications for any package, let us resolve that.
        // 如果这个应用在系统进程中运行,就返回系统进程的Uid,我们分析的情况不包括这个,因此notificationUid和callingUid是一样的
        final int notificationUid = resolveNotificationUid(opPkg, callingUid, userId);

        // Fix the notification as best we can.
        try {
            final ApplicationInfo ai = mPackageManagerClient.getApplicationInfoAsUser(
                    pkg, PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
                    (userId == UserHandle.USER_ALL) ? UserHandle.USER_SYSTEM : userId);
            Notification.addFieldsFromContext(ai, notification);

            int canColorize = mPackageManagerClient.checkPermission(
                    android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, pkg);
            if (canColorize == PERMISSION_GRANTED) {
                notification.flags |= Notification.FLAG_CAN_COLORIZE;
            } else {
                notification.flags &= ~Notification.FLAG_CAN_COLORIZE;
            }

        } catch (NameNotFoundException e) {
            Slog.e(TAG, "Cannot create a context for sending app", e);
            return;
        }

        mUsageStats.registerEnqueuedByApp(pkg);

        // setup local book-keeping
        String channelId = notification.getChannelId();
        if (mIsTelevision && (new Notification.TvExtender(notification)).getChannelId() != null) {
            channelId = (new Notification.TvExtender(notification)).getChannelId();
        }
        // 这里检查是否已经注册过通知通道
        final NotificationChannel channel = mRankingHelper.getNotificationChannel(pkg,
                notificationUid, channelId, false /* includeDeleted */);
        if (channel == null) {
            final String noChannelStr = "No Channel found for "
                    + "pkg=" + pkg
                    + ", channelId=" + channelId
                    + ", id=" + id
                    + ", tag=" + tag
                    + ", opPkg=" + opPkg
                    + ", callingUid=" + callingUid
                    + ", userId=" + userId
                    + ", incomingUserId=" + incomingUserId
                    + ", notificationUid=" + notificationUid
                    + ", notification=" + notification;
            Log.e(TAG, noChannelStr);
            doChannelWarningToast("Developer warning for package \"" + pkg + "\"\n" +
                    "Failed to post notification on channel \"" + channelId + "\"\n" +
                    "See log for more details");
            return;
        }

		// 创建与状态栏相关的StatusBarNotification对象
        final StatusBarNotification n = new StatusBarNotification(
                pkg, opPkg, id, tag, notificationUid, callingPid, notification,
                user, null, System.currentTimeMillis());
        final NotificationRecord r = new NotificationRecord(getContext(), n, channel);

		// 前台服务的情况,略过
        if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0
                && (channel.getUserLockedFields() & NotificationChannel.USER_LOCKED_IMPORTANCE) == 0
                && (r.getImportance() == IMPORTANCE_MIN || r.getImportance() == IMPORTANCE_NONE)) {
			
        }

		// 检测通知是否合格,例如应用发送通知的数量。这里假设通知是合格的
        if (!checkDisqualifyingFeatures(userId, notificationUid, id, tag, r,
                r.sbn.getOverrideGroupKey() != null)) {
            return;
        }

        // 暂时没有用到,略过
        if (notification.allPendingIntents != null) {
			// ...
        }
		// 发送一个消息来处理通知
        mHandler.post(new EnqueueNotificationRunnable(userId, r));
    }
    }
}    

代码很多,与我们分析相关的就是两点

  1. 创建StatusBarNotification对象,创建NotificationRecord对象。创建对象所做的是变量的初始化,后面如果需要这些变量,可再查阅。
  2. 发送一个消息,处理NotificationRecord对象

现在来看下EnqueueNotificationRunnable的处理流程

    protected class EnqueueNotificationRunnable implements Runnable {
        private final NotificationRecord r;
        private final int userId;

        EnqueueNotificationRunnable(int userId, NotificationRecord r) {
            this.userId = userId;
            this.r = r;
        };

        @Override
        public void run() {
            synchronized (mNotificationLock) {
            	// 1. 保存
                mEnqueuedNotifications.add(r);
                // 处理创建通知时设置过超时参数的情况,我们没有设置过,这里分析略过
                scheduleTimeoutLocked(r);

                final StatusBarNotification n = r.sbn;
                if (DBG) Slog.d(TAG, "EnqueueNotificationRunnable.run for: " + n.getKey());
                NotificationRecord old = mNotificationsByKey.get(n.getKey());
                // 目前的情况为null
                if (old != null) {
                    // Retain ranking information from previous record
                    r.copyRankingInformation(old);
                }

                final int callingUid = n.getUid();
                final int callingPid = n.getInitialPid();
                final Notification notification = n.getNotification();
                final String pkg = n.getPackageName();
                final int id = n.getId();
                final String tag = n.getTag();

                // Handle grouped notifications and bail out early if we
                // can to avoid extracting signals.
                // 处理分组的通知,这里略过
                handleGroupedNotificationLocked(r, old, callingUid, callingPid);

                // if this is a group child, unsnooze parent summary
                if (n.isGroup() && notification.isGroupChild()) {
                    mSnoozeHelper.repostGroupSummary(pkg, r.getUserId(), n.getGroupKey());
                }

				// ...

				// 2. 提取通知的各种信息,并跟新给参数r,相当于更新信息
                mRankingHelper.extractSignals(r);

				// 3. 再次发送一个消息,处理更新后的NotificationRecord
                // tell the assistant service about the notification
                if (mAssistants.isEnabled()) {
                    mAssistants.onNotificationEnqueued(r);
                    mHandler.postDelayed(new PostNotificationRunnable(r.getKey()),
                            DELAY_FOR_ASSISTANT_TIME);
                } else {
                    mHandler.post(new PostNotificationRunnable(r.getKey()));
                }
            }
        }
    }

为了减少不必要的代码干扰,这里的流程分为了三步,第一步就是用mEnqueuedNotifications保存这个NotificationRecord对象。

第二步提取通知的各种信息,并更新给参数r。

public class RankingHelper implements RankingConfig {
    public void extractSignals(NotificationRecord r) {
        final int N = mSignalExtractors.length;
        for (int i = 0; i < N; i++) {
            NotificationSignalExtractor extractor = mSignalExtractors[i];
            try {
                RankingReconsideration recon = extractor.process(r);
                // 如果recon不为null,代表无法处理的情况
                if (recon != null) {
                    mRankingHandler.requestReconsideration(recon);
                }
            } catch (Throwable t) {
                Slog.w(TAG, "NotificationSignalExtractor failed.", t);
            }
        }
    }
}

mRankingHelper在构造的时候,会给数组mSignalExtractors填充数据,这个数组指向的是如下类的对象。

frameworks/base/core/res/res/values/config.xml

    <!-- The list of classes that should be added to the notification ranking pipline.
     See {@link com.android.server.notification.NotificationSignalExtractor}
      If you add a new extractor to this list make sure to update
      NotificationManagerService.handleRankingSort()-->
    <string-array name="config_notificationSignalExtractors">
        <!-- many of the following extractors depend on the notification channel, so this
        extractor must come first -->
        <item>com.android.server.notification.NotificationChannelExtractor</item>
        <item>com.android.server.notification.NotificationAdjustmentExtractor</item>
        <!-- depends on AdjustmentExtractor-->
        <item>com.android.server.notification.ValidateNotificationPeople</item>
        <item>com.android.server.notification.PriorityExtractor</item>
        <item>com.android.server.notification.ImportanceExtractor</item>
        <!-- depends on ImportanceExtractor-->
        <item>com.android.server.notification.NotificationIntrusivenessExtractor</item>
        <item>com.android.server.notification.VisibilityExtractor</item>
        <item>com.android.server.notification.BadgeExtractor</item>
    </string-array>

利用这些类对NotificationRecord进行处理,可以挑选其中的一个类再看下处理过程,我们挑选ImportanceExtractor

/**
 * Determines the importance of the given notification.
 */
public class ImportanceExtractor implements NotificationSignalExtractor {
    public RankingReconsideration process(NotificationRecord record) {
        if (record == null || record.getNotification() == null) {
            if (DBG) Slog.d(TAG, "skipping empty notification");
            return null;
        }

        if (mConfig == null) {
            if (DBG) Slog.d(TAG, "missing config");
            return null;
        }
        record.setUserImportance(record.getChannel().getImportance());

        return null;
    }
}

利用通知通道的importance属性更新参数NotificationRecord record的属性。

更新了信息后,就到了EnqueueNotificationRunnable的第三步,这里会再次发送一个信息,看下这个消息的处理

    protected class PostNotificationRunnable implements Runnable {
        private final String key;

        PostNotificationRunnable(String key) {
            this.key = key;
        }

        @Override
        public void run() {
            synchronized (mNotificationLock) {
                try {
                	// 1. 找到key对应的NotificationRecord对象
                    NotificationRecord r = null;
                    int N = mEnqueuedNotifications.size();
                    for (int i = 0; i < N; i++) {
                        final NotificationRecord enqueued = mEnqueuedNotifications.get(i);
                        if (Objects.equals(key, enqueued.getKey())) {
                            r = enqueued;
                            break;
                        }
                    }
                    if (r == null) {
                        Slog.i(TAG, "Cannot find enqueued record for key: " + key);
                        return;
                    }
					// old为null
                    NotificationRecord old = mNotificationsByKey.get(key);
                    final StatusBarNotification n = r.sbn;
                    final Notification notification = n.getNotification();
               
               		// 2. 用mNotificationList进行保存
                    int index = indexOfNotificationLocked(n.getKey());
                    if (index < 0) {
                    	// 如果不存在就保存到mNotificationList中
                        mNotificationList.add(r);
                        mUsageStats.registerPostedByApp(r);
                    } else {
                    	// 如果存在就更新集合中的值
                        old = mNotificationList.get(index);
                        mNotificationList.set(index, r);
                        mUsageStats.registerUpdatedByApp(r, old);
                        // Make sure we don't lose the foreground service state.
                        notification.flags |=
                                old.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE;
                        r.isUpdate = true;
                    }
						
					// 3. 用mNotificationsByKey保存
                    mNotificationsByKey.put(n.getKey(), r);

                    // Ensure if this is a foreground service that the proper additional
                    // flags are set.
                    // 处理前台service情况
                    if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
                        notification.flags |= Notification.FLAG_ONGOING_EVENT
                                | Notification.FLAG_NO_CLEAR;
                    }
					
					// 添加zen mode的一些信息
                    applyZenModeLocked(r);
                    // 4. 排序
                    mRankingHelper.sort(mNotificationList);

                    if (notification.getSmallIcon() != null) {
                    	// 如果不是更新通知,oldSbn为null
                        StatusBarNotification oldSbn = (old != null) ? old.sbn : null;
                        // 5. 通知所有的监听者,有通知到来
                        mListeners.notifyPostedLocked(n, oldSbn);
                        // 处理分组的通知,这里忽略
                        if (oldSbn == null || !Objects.equals(oldSbn.getGroup(), n.getGroup())) {
                            mHandler.post(new Runnable() {
                                @Override
                                public void run() {
                                    mGroupHelper.onNotificationPosted(
                                            n, hasAutoGroupSummaryLocked(n));
                                }
                            });
                        }
                    } else {
						// ...
                    }
					// 6. 处理通知的声音以及led灯闪烁情况
                    buzzBeepBlinkLocked(r);
                } finally {
                	// 7. 处理完毕后,就从mEnqueuedNotifications中移除
                    int N = mEnqueuedNotifications.size();
                    for (int i = 0; i < N; i++) {
                        final NotificationRecord enqueued = mEnqueuedNotifications.get(i);
                        if (Objects.equals(key, enqueued.getKey())) {
                            mEnqueuedNotifications.remove(i);
                            break;
                        }
                    }
                }
            }
        }
    }

第一步,从mEnqueuedNotifications中找到已经处于队列中的NotificationRecord

第二步和第三步,分别用mNotificationListmNotificationsByKey进行保存找到的NotificationRecord对象。

第四步,对mNotificationList进行排序,这个排序是根据通知的各个属性进行排序的,以后如果分析通知的显示顺序的时候,再来重点分析。

第五步,把通知发送给所有的监听者。

第六步,处理通知的声音以及led灯闪烁情况。

第七步,我们需要注意,发送了通知以后,需要从mEnqueuedNotifications队列中移除。

现在重点关注第五步,看它是如何通知发送给相关的监听者的,mListenersNotificationManagerService 的一个内部类 NotificationListeners 的对象。

public class NotificationManagerService extends SystemService {

    protected List<ManagedServiceInfo> getServices() {
        synchronized (mMutex) {
            List<ManagedServiceInfo> services = new ArrayList<>(mServices);
            return services;
        }
    }

    public class NotificationListeners extends ManagedServices {
        /**
         * asynchronously notify all listeners about a new notification
         *
         * Also takes care of removing a notification that has been visible to a listener before,
         * but isn't anymore.
         */
        public void notifyPostedLocked(StatusBarNotification sbn, StatusBarNotification oldSbn) {
            // Lazily initialized snapshots of the notification.
            TrimCache trimCache = new TrimCache(sbn);
			
			// 1. 遍历注册过的服务信息
            for (final ManagedServiceInfo info : getServices()) {
            	// 如果是系统注册的监听器,那么这里返回的就是true,例如状态栏注册的监听器就是如此
                boolean sbnVisible = isVisibleToListener(sbn, info);
                boolean oldSbnVisible = oldSbn != null ? isVisibleToListener(oldSbn, info) : false;
                // 如果新旧通知都不可见,就跳过
                if (!oldSbnVisible && !sbnVisible) {
                    continue;
                }
				// 2. 把mNotificationList中的所有信息包装成NotificationRankingUpdate对象
                final NotificationRankingUpdate update = makeRankingUpdateLocked(info);

				// 3. 发送消息给监听者
                // 3.1 如果新通知不可见,就移除旧的通知,然后跳过
                if (oldSbnVisible && !sbnVisible) {
                    final StatusBarNotification oldSbnLightClone = oldSbn.cloneLight();
                    mHandler.post(new Runnable() {
                        @Override
                        public void run() {
                            notifyRemoved(info, oldSbnLightClone, update, REASON_USER_STOPPED);
                        }
                    });
                    continue;
                }

				// 3.2 如果以上条件都不满足,就通知监听者
                final StatusBarNotification sbnToPost =  trimCache.ForListener(info);
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        notifyPosted(info, sbnToPost, update);
                    }
                });
            }
        }
        
        private void notifyPosted(final ManagedServiceInfo info,
                final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) {
            final INotificationListener listener = (INotificationListener) info.service;
            StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
            try {
                listener.onNotificationPosted(sbnHolder, rankingUpdate);
            } catch (RemoteException ex) {
                Log.e(TAG, "unable to notify listener (posted): " + listener, ex);
            }
        }
    }
}    

通过遍历注册过的服务信息找到监听者,然后提取保存的通知的信息发送给它们。通知肯定会显示在下拉状态栏中,那么状态栏肯定是其中一个监听者,那么状态栏是如何注册称为监听者,以及状态栏如何显示通知的,请听下回分解。

发布了44 篇原创文章 · 获赞 30 · 访问量 400万+

猜你喜欢

转载自blog.csdn.net/zwlove5280/article/details/89951851
今日推荐