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.     *     * 

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. * *

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

        <string-array name="config_notificationSignalExtractors">                <item>com.android.server.notification.NotificationChannelExtractoritem>        <item>com.android.server.notification.NotificationAdjustmentExtractoritem>                <item>com.android.server.notification.ValidateNotificationPeopleitem>        <item>com.android.server.notification.PriorityExtractoritem>        <item>com.android.server.notification.ImportanceExtractoritem>                <item>com.android.server.notification.NotificationIntrusivenessExtractoritem>        <item>com.android.server.notification.VisibilityExtractoritem>        <item>com.android.server.notification.BadgeExtractoritem>    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);            }        }    }}    

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

更多相关文章

  1. OnclickListener与View.OnclickListener
  2. Android中Parcelable和Serializable接口用法
  3. Android通过socket连接服务器(PC)
  4. Android音视频处理之MediaMuxer
  5. Android创建快捷方式图标
  6. android发送QQ邮件(带附件)
  7. android 创建自定义对话框
  8. Android(安卓)删除 未接来电 通知
  9. 第一代Android壳源码--某某公司早期壳代码加固原理分析

随机推荐

  1. 利用FRIDA攻击Android应用程序(三)
  2. [置顶] Android驱动开发-底层驱动开发
  3. 下载和编译Android源码问题集(持续更新)
  4. Android混合开发之------ AndroidStudio
  5. 【项目架构】Android(安卓)MVP 和MVVM框
  6. Android(安卓)SDK Tutorials系列 - Hello
  7. 【Xcode应用】iOS性能分析
  8. Android中的UID和AppId
  9. Android(安卓)布局面试硬知识点
  10. Android(安卓)Bundle总结