Android11 Notification功能解析

       我们知道,当手机有通知时,下拉通知中心中会显示所有的通知,该功能是在SystemUI中实现的,接着上篇文章Android11 SystemUI解析 ,本文对通知相关的功能逻辑进行分析,基于Android11 CarSystemUI的通知功能逻辑展开分析。
       关于通知功能逻辑,简单来说,无非就是四步,注册、发送、接收、显示,那么接下来针对以上四步进行源码详细分析。

一.注册

       关于CarSystemUI启动及相关逻辑可以参考文章Android11 SystemUI解析 ,本文就不赘述了,直接以NotificationPanelViewController类为入口进行分析:

1.NotificationPanelViewController

       从构造方法来看:

@Inject
public NotificationPanelViewController(xxxxxxxx,
            /* Things needed for notifications */
            IStatusBarService barService,
            CommandQueue commandQueue,
            NotificationDataManager notificationDataManager,
            CarUxRestrictionManagerWrapper carUxRestrictionManagerWrapper,
            CarNotificationListener carNotificationListener,
            NotificationClickHandlerFactory notificationClickHandlerFactory,
            xxxxxxxxxxx
    ) {
}

mNotificationViewController = new NotificationViewController(
            mNotificationView,PreprocessingManager.getInstance(mContext),
            mCarNotificationListener,mCarUxRestrictionManagerWrapper,
            mNotificationDataManager);
mNotificationViewController.enable();

       可以看到,在创建以上实例时,会通过Inject的方式来创造对应参数的实例,该功能是通过dagger2来实现,具体对应的Module为CarNotificationModule类,看一下CarNotificationListener实例创造时的实现,关于NotificationViewController后面再分析:

@Provides
@Singleton
static CarNotificationListener provideCarNotificationListener(Context context,
            CarUxRestrictionManagerWrapper carUxRestrictionManagerWrapper,
            CarHeadsUpNotificationManager carHeadsUpNotificationManager,
            NotificationDataManager notificationDataManager) {
    CarNotificationListener listener = new CarNotificationListener();
    listener.registerAsSystemService(context, carUxRestrictionManagerWrapper,
                carHeadsUpNotificationManager, notificationDataManager);
    return listener;
}

       可以看到,在provideCarNotificationListener()提供CarNotificationListener实例时,还执行了registerAsSystemService()方法,接下来看一下CarNotificationListener类:

2.CarNotificationListener

public class CarNotificationListener extends NotificationListenerService implements
        CarHeadsUpNotificationManager.OnHeadsUpNotificationStateChange {
    .....................
    .....................
    public void registerAsSystemService(Context context,
            CarUxRestrictionManagerWrapper carUxRestrictionManagerWrapper,
            CarHeadsUpNotificationManager carHeadsUpNotificationManager,
            NotificationDataManager notificationDataManager) {
        try {
            mNotificationDataManager = notificationDataManager;
            registerAsSystemService(context,
                    new ComponentName(context.getPackageName(), getClass().getCanonicalName()),
                    ActivityManager.getCurrentUser());
            ...................................
        } catch (RemoteException e) {
            Log.e(TAG, "Unable to register notification listener", e);
        }
    }

    @Override
    public void onListenerConnected() {
        mActiveNotifications = Stream.of(getActiveNotifications()).collect(
                Collectors.toMap(StatusBarNotification::getKey, sbn -> new AlertEntry(sbn)));
        mRankingMap = super.getCurrentRanking();
    }
}

       CarNotificationListener继承了NotificationListenerService类,该类继承了Service,是framework内部的组成部分,通过registerAsSystemService()来看一下该类的实现:

3.NotificationListenerService

public void registerAsSystemService(Context context, ComponentName componentName,
            int currentUser) throws RemoteException {
    if (mWrapper == null) {
        mWrapper = new NotificationListenerWrapper();
    }
    mSystemContext = context;
    INotificationManager noMan = getNotificationInterface();
    mHandler = new MyHandler(context.getMainLooper());
    mCurrentUser = currentUser;
    noMan.registerListener(mWrapper, componentName, currentUser);
}

       该方法内部主要执行了两项操作:
       a.创建了NotificationListenerWrapper对象,该类继承了INotificationListener.Stub,主要用来接收回调,后面在显示环节进行详细分析;

protected class NotificationListenerWrapper extends INotificationListener.Stub {
    @Override
    public void onNotificationPosted(IStatusBarNotificationHolder sbnHolder,
                NotificationRankingUpdate update) {
    }

    @Override
    public void onNotificationRemoved(IStatusBarNotificationHolder sbnHolder,
                NotificationRankingUpdate update, NotificationStats stats, int reason) {
    }
    .....................
}

       b.将以上对象作为参数通过INotificationManager的registerListener进行注册;

protected final INotificationManager getNotificationInterface() {
    if (mNoMan == null) {
        mNoMan = INotificationManager.Stub.asInterface(
                ServiceManager.getService(Context.NOTIFICATION_SERVICE));
    }
    return mNoMan;
}

       通过getNotificationInterface()的是实现可以知道,registerListener()执行到了NotificationManagerService里面去了,接下来一起看一下:

4.NotificationManagerService

@Override
public void registerListener(final INotificationListener listener,
            final ComponentName component, final int userid) {
    enforceSystemOrSystemUI("INotificationManager.registerListener");
    mListeners.registerSystemService(listener, component, userid);
}

       mListeners是NotificationListeners实例,是在init()中进行初始化的,NotificationListeners是其内部类,看一下具体实现:

public class NotificationListeners extends ManagedServices {
    .......................
    .......................
    @Override
    public void onServiceAdded(ManagedServiceInfo info) {
        final INotificationListener listener = (INotificationListener) info.service;
        final NotificationRankingUpdate update;
        synchronized (mNotificationLock) {
            update = makeRankingUpdateLocked(info);
            updateUriPermissionsForActiveNotificationsLocked(info, true);
        }
        try {
            listener.onListenerConnected(update);
        }
    }
}

       NotificationListeners继承了ManagedServices,registerSystemService方法是在ManagedServices里面实现的,看一下:

public void registerSystemService(IInterface service, ComponentName component, int userid) {
    checkNotNull(service);
    ManagedServiceInfo info = registerServiceImpl(
                service, component, userid, Build.VERSION_CODES.CUR_DEVELOPMENT);
    if (info != null) {
        onServiceAdded(info);
    }
}

private ManagedServiceInfo registerServiceImpl(final IInterface service,
            final ComponentName component, final int userid, int targetSdk) {
    ManagedServiceInfo info = newServiceInfo(service, component, userid,
                true /*isSystem*/, null /*connection*/, targetSdk);
    return registerServiceImpl(info);
}

private ManagedServiceInfo registerServiceImpl(ManagedServiceInfo info) {
    synchronized (mMutex) {
        try {
            info.service.asBinder().linkToDeath(info, 0);
            mServices.add(info);
            return info;
        }
    }
    return null;
}

       根据调用关系,registerServiceImpl()方法内先将前面创建的INotificationListener(mWrapper)作为参数创建了ManagedServiceInfo实例info,然后执行linkToDeath进行死亡监测,最后将info加入mServices中进行管理,执行完后再执行onServiceAdded(info),该方法是在NotificationListeners类内部实现的,再回去看一下该方法,最终会调用到CarNotificationListener.java里面的onListenerConnected()方法。
       以上注册过程逻辑比较绕,用一张图来总结一下:


image.png

二.发送

       发送过程比较简单,按照系统提供的方法来发送即可,主要涉及NotificationChannel、Notification、NotificationManager这三个类,简单看一下:
       首先某个应用在发送通知前需要创建该应用对应的NotificationChannel,然后在通知中传入对应channel ID就可以了,创建如下:

1.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;
}

       在创建NotificationChannel时需要传入唯一的id、name和importance,创建如下:

NotificationManager notificationManager = (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE);
NotificationChannel notificationChannel = new NotificationChannel(DEBUG_CHANNEL_ID, DEBUG_CHANNEL_NAME,
                NotificationManager.IMPORTANCE_DEFAULT);
notificationManager.createNotificationChannel(notificationChannel);

2.Notification

       创建完NotificationChannel后,再创建Notification,Notification创建采用的是Builder模式,主要涉及的内容比较多,创建如下:

Notification missedCallNotification = new Notification.Builder(context, DEBUG_CHANNEL_ID)
        .setContentTitle("18330656010")
        .setContentText("未接来电")
        .setCategory(Notification.CATEGORY_MISSED_CALL)
        .setSmallIcon(R.drawable.notification_missed_call)
        .setOngoing(true)
        .setLargeIcon(Icon.createWithResource(context, R.drawable.notification_missed_call))
        .build();

       Notification涉及的内容比较多,可以根据需要进行设定;

3.NotificationManager

       创建完Notification后,通过NotificationManger来进行发送就可以了:

notificationManager.notify(MISSED_CALL_ID, missedCallNotification);

       执行完notify后续的逻辑处理过程,在接收环节进行分析;

三.接收

1.NotificationManager

       发送时会调用到notify()方法:

public void notify(int id, Notification notification){
    notify(null, id, notification);
}

public void notify(String tag, int id, Notification notification){
    notifyAsUser(tag, id, notification, mContext.getUser());
}

       跟随调用关系,会调用到notifyAsUser()方法:

public void notifyAsUser(String tag, int id, Notification notification, UserHandle user){
    INotificationManager service = getService();
    String pkg = mContext.getPackageName();

    try {
        if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")");
        service.enqueueNotificationWithTag(pkg, mContext.getOpPackageName(), tag, id,
              fixNotification(notification), user.getIdentifier());
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}

       在notifyAsUser()会调用到NotificationManagerService中的enqueueNotificationWithTag()方法,先看一下fixNotification()方法:

private Notification fixNotification(Notification notification) {
    .....................
    if (mContext.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1) {
        if (notification.getSmallIcon() == null) {
                throw new IllegalArgumentException("Invalid notification (no valid small icon): "
                        + notification);
        }
    }
    ...............
}

       需要注意一下:当应用targetSdkVersion大于22时,在创建Notification时需要传入smallIcon,否则会抛异常导致发送不成功;接下来看一下enqueueNotificationWithTag()方法:

2.NotificationManagerService

       NotificationManagerService继承了SystemService,在SystemServer中会进行启动,在start()方法内部会执行publishBinderService来进行Binder注册提供服务:

publishBinderService(Context.NOTIFICATION_SERVICE, mService,
 /* allowIsolated= */ false,DUMP_FLAG_PRIORITY_CRITICAL DUMP_FLAG_PRIORITY_NORMAL);

2.1.enqueueNotificationWithTag()

final IBinder mService = new INotificationManager.Stub() {
    ....................................
    @Override
    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);
    }
}

2.2.enqueueNotificationInternal()

       可以看到,enqueueNotificationWithTag()会调用到enqueueNotificationInternal(),该方法是真正的逻辑实现:

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, boolean postSilently) {
    ..........................
    //check
    ..........................

    // Fix the notification as best we can.
    try {
        fixNotification(notification, pkg, tag, id, userId);
    }

    final StatusBarNotification n = new StatusBarNotification(
            pkg, opPkg, id, tag, notificationUid, callingPid, notification,
            user, null, mSystemClock.currentTimeMillis());

    // setup local book-keeping
    String channelId = notification.getChannelId();
    if (mIsTelevision && (new Notification.TvExtender(notification)).getChannelId() != null) {
        channelId = (new Notification.TvExtender(notification)).getChannelId();
    }
    String shortcutId = n.getShortcutId();
    final NotificationChannel channel = mPreferencesHelper.getConversationNotificationChannel(
            pkg, notificationUid, channelId, shortcutId,
            true /* parent ok */, false /* includeDeleted */);
    if (channel == null) {
        .....................................
        return;
    }

   final NotificationRecord r = new NotificationRecord(getContext(), n, channel);
   ...........................
   ..........................
    // Need escalated privileges to get package importance
    final long token = Binder.clearCallingIdentity();
    boolean isAppForeground;
    try {
        isAppForeground = mActivityManager.getPackageImportance(pkg) == IMPORTANCE_FOREGROUND;
    } finally {
        Binder.restoreCallingIdentity(token);
    }
    mHandler.post(new EnqueueNotificationRunnable(userId, r, isAppForeground));
}

       该方法中主要做了以下几件事:
       1.进行各种check来确保notification的合法性;
       2.将Notification作为参数创建StatusBarNotification;
       3.获取Notification对应的channel id,根据channel id 来获取应用对应的NotificationChannel,如果为空的话,就直接返回了,因此应用在发送notification前需要先创建对应NotificationChannel;
       4.通过Handler post EnqueueNotificationRunnable来执行后续逻辑;

2.3.EnqueueNotificationRunnable

protected class EnqueueNotificationRunnable implements Runnable {
    .......................
    @Override
    public void run() {
        ........................
        mEnqueuedNotifications.add(r);
        final StatusBarNotification n = r.getSbn();
        NotificationRecord old = mNotificationsByKey.get(n.getKey());
        ..........................
        postPostNotificationRunnableMaybeDelayedLocked(
                        r, new PostNotificationRunnable(r.getKey()));
    }
}

       在EnqueueNotificationRunnable内部会将r(NotificationRecord)加入mEnqueuedNotifications进行管理,然后判断该NotificationRecord是否已经存在过,最后执行PostNotificationRunnable;

2.4.PostNotificationRunnable

protected class PostNotificationRunnable implements Runnable {

    @Override
    public void run() {
        synchronized (mNotificationLock) {
            try {
                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;
                    }
                }
                .......................
                NotificationRecord old = mNotificationsByKey.get(key);
                final StatusBarNotification n = r.getSbn();
                final Notification notification = n.getNotification();
                ......................
                int index = indexOfNotificationLocked(n.getKey());
                if (index < 0) {
                    mNotificationList.add(r);
                    mUsageStats.registerPostedByApp(r);
                    r.setInterruptive(isVisuallyInterruptive(null, r));
                } else {
                    old = mNotificationList.get(index);  // Potentially *changes* old
                    mNotificationList.set(index, r);
                    mUsageStats.registerUpdatedByApp(r, old);
                    // Make sure we don't lose the foreground service state.
                    notification.flags |=
                                old.getNotification().flags & FLAG_FOREGROUND_SERVICE;
                    r.isUpdate = true;
                    final boolean isInterruptive = isVisuallyInterruptive(old, r);
                    r.setTextChanged(isInterruptive);
                    r.setInterruptive(isInterruptive);
               }

               mNotificationsByKey.put(n.getKey(), r);
               ...........................
               if (notification.getSmallIcon() != null) {
                   StatusBarNotification oldSbn = (old != null) ? old.getSbn() : null;
                   mListeners.notifyPostedLocked(r, old);
                   .........................
                } finally {
                    int N = mEnqueuedNotifications.size();
                    NotificationRecord enqueued = null;
                    for (int i = 0; i < N; i++) {
                        enqueued = mEnqueuedNotifications.get(i);
                        if (Objects.equals(key, enqueued.getKey())) {
                            mEnqueuedNotifications.remove(i);
                            break;
                        }
                    }

                    // If the enqueued notification record had a cancel attached after it, execute
                    // it right now
                    if (enqueued != null && mDelayedCancelations.get(enqueued) != null) {
                        for (CancelNotificationRunnable r : mDelayedCancelations.get(enqueued)) {
                            r.doNotificationCancelLocked();
                        }
                        mDelayedCancelations.remove(enqueued);
                    }
                }
            }
        }
}

       PostNotificationRunnable的run()中主要处理逻辑如下:
       1.从mEnqueuedNotifications中找到跟key对应的NotificationRecord;
       2.通过indexOfNotificationLocked()看mNotificationList里面是否已经包含该NotificationRecord,如果不存在,说明是新的record,需要加入mNotificationList进行管理;否则的话,将mNotificationList中index对应的NotificationRecord进行更新;
       3.将要处理的NotificationRecord放入mNotificationsByKey进行管理;
       4.执行mListeners.notifyPostedLocked(r, old)来进行通知;
       5.在finally里面将要处理的NotificationRecord从mEnqueuedNotifications里面移除;
       6.如果在加入后有取消操作,需要立刻执行取消操作,并将NotificationRecord从mDelayedCancelations中移除;

四.显示

       前面讲到,在PostNotificationRunnable中会执行mListeners.notifyPostedLocked(r, old)进行通知,mListeners是NotificationListeners实例:

1.NotificationListeners

public void notifyPostedLocked(NotificationRecord r, NotificationRecord old) {
    notifyPostedLocked(r, old, true);
}

       跟随调用关系:

private void notifyPostedLocked(NotificationRecord r, NotificationRecord old,
                boolean notifyAllListeners) {
    try {
        // Lazily initialized snapshots of the notification.
        StatusBarNotification sbn = r.getSbn();
        StatusBarNotification oldSbn = (old != null) ? old.getSbn() : null;
        TrimCache trimCache = new TrimCache(sbn);

        for (final ManagedServiceInfo info : getServices()) {
              ..............................
              final StatusBarNotification sbnToPost = trimCache.ForListener(info);
              mHandler.post(() -> notifyPosted(info, sbnToPost, update));
        }
    }
}

       通过getServices()来找到已经注册过的ManagedServiceInfo列表,最后执行notifyPosted();

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);
    }
}

       在notifyPosted()内部通过info.service来找到对应的INotificationListener实例,对应NotificationListenerService内部的NotificationListenerWrapper,然后将StatusBarNotification封装成StatusBarNotificationHolder,最后执行NotificationListenerWrapper的onNotificationPosted()方法:

2.NotificationListenerWrapper

protected class NotificationListenerWrapper extends INotificationListener.Stub {
    @Override
    public void onNotificationPosted(IStatusBarNotificationHolder sbnHolder,
                NotificationRankingUpdate update) {
        StatusBarNotification sbn;
        try {
            sbn = sbnHolder.get();
        }
        .................................
        // protect subclass from concurrent modifications of (@link mNotificationKeys}.
        synchronized (mLock) {
            applyUpdateLocked(update);
            if (sbn != null) {
                SomeArgs args = SomeArgs.obtain();
                args.arg1 = sbn;
                args.arg2 = mRankingMap;
                mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_POSTED,
                            args).sendToTarget();
            }
        }
..............
}

       最终会通过Handler来发送消息来进行处理;

3.NotificationListenerService

    case MSG_ON_NOTIFICATION_POSTED: {
         SomeArgs args = (SomeArgs) msg.obj;
         StatusBarNotification sbn = (StatusBarNotification) args.arg1;
         RankingMap rankingMap = (RankingMap) args.arg2;
         args.recycle();
         onNotificationPosted(sbn, rankingMap);
    } 
    break;

       onNotificationPosted(sbn, rankingMap)是在CarNotificationListener内部实现的;

4.CarNotificationListener

@Override
public void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) {
    .....................
    AlertEntry alertEntry = new AlertEntry(sbn);
    onNotificationRankingUpdate(rankingMap);
    notifyNotificationPosted(alertEntry);
}

       在CarNotificationListener内部会将StatusBarNotification封装成AlertEntry,然后执行notifyNotificationPosted():

private void notifyNotificationPosted(AlertEntry alertEntry) {
    ..........................
    postNewNotification(alertEntry);
}

       一步一步调用:

private void postNewNotification(AlertEntry alertEntry) {
    mActiveNotifications.put(alertEntry.getKey(), alertEntry);
    sendNotificationEventToHandler(alertEntry, NOTIFY_NOTIFICATION_POSTED);
}

       先将alertEntry存入mActiveNotifications进行管理;然后执行sendNotificationEventToHandler发送NOTIFY_NOTIFICATION_POSTED消息;

private void sendNotificationEventToHandler(AlertEntry alertEntry, int eventType) {
    Message msg = Message.obtain(mHandler);
    msg.what = eventType;
    msg.obj = alertEntry;
    mHandler.sendMessage(msg);
}

       该Handler是通过setHandler来赋值的,具体是在什么地方呢?
       这个需要回到最前面NotificationPanelViewController里面了,前面说到NotificationViewController是在显示环节进行分析,轮到NotificationViewController登场了;

5.NotificationViewController

       NotificationViewController是在NotificationPanelViewController实例化,并执行enable()方法,先看一下构造方法:

public NotificationViewController(CarNotificationView carNotificationView,
            PreprocessingManager preprocessingManager,
            CarNotificationListener carNotificationListener,
            CarUxRestrictionManagerWrapper uxResitrictionListener,
            NotificationDataManager notificationDataManager) {
    mCarNotificationView = carNotificationView;
    mPreprocessingManager = preprocessingManager;
    mCarNotificationListener = carNotificationListener;
    mUxResitrictionListener = uxResitrictionListener;
    mNotificationDataManager = notificationDataManager;
}

       在构造方法内部,会传入CarNotificationView、CarNotificationListener、PreprocessingManager等实例,都是跟显示有关的核心类;
       1.CarNotificationView:负责处理通知显示;
       2.PreprocessingManager:负责管理通知,通过CarNotificationListener来获取通知;
       3.CarNotificationListener:跟NotificationManagerService间接交互的类;

public void enable() {
    mCarNotificationListener.setHandler(mNotificationUpdateHandler);
    ..................
}

private class NotificationUpdateHandler extends Handler {
    @Override
    public void handleMessage(Message message) {
        if (mIsVisible) {
            updateNotifications(
                    mShowLessImportantNotifications,message.what,(AlertEntry) message.obj);
        } else {
            resetNotifications(mShowLessImportantNotifications);
       }
    }
}

       可以看到Handler是在NotificationViewController里面实现的,当有消息到来时,如果CarNotificationView显示时执行updateNotifications()来直接显示通知;不显示时执行resetNotifications()来对通知进行管理;

private void updateNotifications(
            boolean showLessImportantNotifications, int what, AlertEntry alertEntry) {

    if (mPreprocessingManager.shouldFilter(alertEntry,
                mCarNotificationListener.getCurrentRanking())) {
        // if the new notification should be filtered out, return early
        return;
    }

    mCarNotificationView.setNotifications(
            mPreprocessingManager.updateNotifications(
                    showLessImportantNotifications,
                    alertEntry,
                    what,
                    mCarNotificationListener.getCurrentRanking()));
}

       以上就是Notification的整个工作过程,最后用一张流程图来总结一下:


image.png

你可能感兴趣的:(Android11 Notification功能解析)