Android 9.0 SystemUI Notification

本文主要分享 SystemUI Notification 具体如何呈现的?基于 AOSP 9.0 分析。

概述

Android 9.0 SystemUI Notification

在《Android 9.0 SystemUI 主要视图 SystemBars》知道通知在折叠时状态栏、下拉状态栏、锁屏都有通知,其中锁屏和下拉状态栏是一个布局,折叠状态栏 是在 CollapsedStatusBarFragment,status_bar.xml,PhoneStatusBarView,锁屏是 NotificationStackScrollLayout,@+id/notification_stack_scroller,先来看看锁屏的通知,NotificationStackScrollLayout 是 ViewGroup,如果来了条通知,肯定是有地方进行 addView,我们就沿着这个思路去 AOSP 寻找答案。

序列图

Android 9.0 SystemUI Notification

序列图为来通知到 SystemUI 锁屏通知呈现整个流程。

锁屏通知

NotificationStackScrollLayout#addContainerView

锁屏是 NotificationStackScrollLayout,直接找 NotificationStackScrollLayout,看到有个 addContainerView方法,一看,果然是目标 addView:

@Override
public void addContainerView(View v) {
   addView(v);
}

反查,看到  addContainerView 被 NotificationViewHierarchyManager#updateNotificationViews 方法调用了。

NotificationViewHierarchyManager#updateNotificationViews

public void updateNotificationViews() {
   ArrayList<NotificationData.Entry> activeNotifications = mEntryManager.getNotificationData()
           .getActiveNotifications();
   ArrayList<ExpandableNotificationRow> toShow = new ArrayList<>(activeNotifications.size());
  //省略其他代码
   for (int i = 0; i < toShow.size(); i++) {
       View v = toShow.get(i);
       if (v.getParent() == null) {
           mVisualStabilityManager.notifyViewAddition(v);
           mListContainer.addContainerView(v);
       }
   }
  //省略其他代码
}

这里 mListContainer 是 NotificationListContainer,NotificationStackScrollLayout#addContainerView 进行了重写。

反查, NotificationViewHierarchyManager#updateNotificationViews 被 StatusBar#updateNotificationViews 方法调用了。

StatusBar#updateNotificationViews

@Override
public void updateNotificationViews() {
   //省略其他代码
   mViewHierarchyManager.updateNotificationViews();
   //省略其他代码
   //这里和折叠状态栏相关
   mNotificationIconAreaController.updateNotificationIcons();
}

StatusBar#updateNotificationViews 被 NotificationEntryManager#updateNotifications 调用了。

NotificationEntryManager#updateNotifications

public void updateNotifications() {
   mNotificationData.filterAndSort();
   mPresenter.updateNotificationViews();
}

presenter 是 NotificationPresenter 对象,从 StatusBar#makeStatusBarView 传过来了,继续看 NotificationEntryManager#updateNotifications 哪里被调用了,是 NotificationEntryManager#addNotificationViews。

NotificationEntryManager#addNotificationViews

protected void addNotificationViews(NotificationData.Entry entry) {
   if (entry == null) {
       return;
   }
   // Add the expanded view and icon.
   mNotificationData.add(entry);
   tagForeground(entry.notification);
   updateNotifications();
}

//NotificationEntryManager#addNotificationViews 由 addEntry 调用了
private void addEntry(NotificationData.Entry shadeEntry) {
   boolean isHeadsUped = shouldPeek(shadeEntry);
   if (isHeadsUped) {
       mHeadsUpManager.showNotification(shadeEntry);
       // Mark as seen immediately
       setNotificationShown(shadeEntry.notification);
   }
   addNotificationViews(shadeEntry);
   mCallback.onNotificationAdded(shadeEntry);
}

NotificationEntryManager#addEntry 由 NotificationEntryManager#onAsyncInflationFinished 调用了。

NotificationEntryManager#onAsyncInflationFinished

@Override
public void onAsyncInflationFinished(NotificationData.Entry entry) {
   mPendingNotifications.remove(entry.key);
   // If there was an async task started after the removal, we don't want to add it back to
   // the list, otherwise we might get leaks.
   boolean isNew = mNotificationData.get(entry.key) == null;
   if (isNew && !entry.row.isRemoved()) {
       addEntry(entry);
   } else if (!isNew && entry.row.hasLowPriorityStateUpdated()) {
       mVisualStabilityManager.onLowPriorityUpdated(entry);
       mPresenter.updateNotificationViews();
   }
   entry.row.setLowPriorityStateUpdated(false);
}

问题来了,NotificationEntryManager#onAsyncInflationFinished 哪里被调到了,似乎断掉了,是怎么和来通知关联起来的?这得需要看看通知的流程。

通知流程

这部分分析按照正常的调用顺序来分析。

NotificationManager#notify

NotificationManager 调用 notify 方法发送 notification,最后调用到 notifyAsUser() 方法:

@UnsupportedAppUsage
public void notifyAsUser(String tag, int id, Notification notification, UserHandle user)
{
   INotificationManager service = getService();
   String pkg = mContext.getPackageName();
   // Fix the notification as best we can.
   Notification.addFieldsFromContext(mContext, notification);
   if (notification.sound != null) {
       notification.sound = notification.sound.getCanonicalUri();
       if (StrictMode.vmFileUriExposureEnabled()) {
           notification.sound.checkFileUriExposed("Notification.sound");
       }
   }
   fixLegacySmallIcon(notification, pkg);
   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();
   final Notification copy = Builder.maybeCloneStrippedForDelivery(notification, isLowRam,
           mContext);
   try {
       service.enqueueNotificationWithTag(pkg, mContext.getOpPackageName(), tag, id,
               copy, user.getIdentifier());
   } catch (RemoteException e) {
       throw e.rethrowFromSystemServer();
   }
}

这里 service 是 INotificationManager,对应的是 NotificationManagerService,看 NotificationManagerService#enqueueNotificationWithTag,又调用了 NotificationManagerService#enqueueNotificationInternal。

NotificationManagerService#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) {
   //省略其他代码
   final NotificationRecord r = new NotificationRecord(getContext(), n, channel);
   r.setIsAppImportanceLocked(mRankingHelper.getIsAppImportanceLocked(pkg, callingUid));
   //省略其他代码
   mHandler.post(new EnqueueNotificationRunnable(userId, r));
}

EnqueueNotificationRunnable#run

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) {
           // 省略其他代码
           if (mAssistants.isEnabled()) {
               mAssistants.onNotificationEnqueued(r);
               mHandler.postDelayed(new PostNotificationRunnable(r.getKey()),
                       DELAY_FOR_ASSISTANT_TIME);
           } else {
               mHandler.post(new PostNotificationRunnable(r.getKey()));
           }
       }
   }
}

PostNotificationRunnable#run

protected class PostNotificationRunnable implements Runnable {
   private final String key;
   PostNotificationRunnable(String key) {
       this.key = key;
   }
   @Override
   public void run() {
       synchronized (mNotificationLock) {
           try {
               // 省略其他代码
               if (notification.getSmallIcon() != null) {
                   StatusBarNotification oldSbn = (old != null) ? old.sbn : null;
                   mListeners.notifyPostedLocked(r, old);
                   if (oldSbn == null || !Objects.equals(oldSbn.getGroup(), n.getGroup())) {
                       mHandler.post(new Runnable() {
                           @Override
                           public void run() {
                               mGroupHelper.onNotificationPosted(
                                       n, hasAutoGroupSummaryLocked(n));
                           }
                       });
                   }
               }  // 省略其他代码
           } finally {
               // 省略其他代码
           }
       }
   }
}

mListeners 是 NotificationListeners,调用 NotificationManagerService#notifyPostedLocked。

NotificationManagerService#notifyPostedLocked

@GuardedBy("mNotificationLock")
public void notifyPostedLocked(NotificationRecord r, NotificationRecord old) {
   notifyPostedLocked(r, old, true);
}

@GuardedBy("mNotificationLock")
private void notifyPostedLocked(NotificationRecord r, NotificationRecord old,
       boolean notifyAllListeners) {
   // Lazily initialized snapshots of the notification.
   StatusBarNotification sbn = r.sbn;
   StatusBarNotification oldSbn = (old != null) ? old.sbn : null;
   TrimCache trimCache = new TrimCache(sbn);
   for (final ManagedServiceInfo info : getServices()) {
       //省略其他代码
       final NotificationRankingUpdate update = makeRankingUpdateLocked(info);
       // This notification became invisible -> remove the old one.
       if (oldSbnVisible && !sbnVisible) {
           final StatusBarNotification oldSbnLightClone = oldSbn.cloneLight();
           mHandler.post(new Runnable() {
               @Override
               public void run() {
                   notifyRemoved(
                           info, oldSbnLightClone, update, null, REASON_USER_STOPPED);
               }
           });
           continue;
       }
       // Grant access before listener is notified
       final int targetUserId = (info.userid == UserHandle.USER_ALL)
               ? UserHandle.USER_SYSTEM : info.userid;
       updateUriPermissions(r, old, info.component.getPackageName(), targetUserId);
       final StatusBarNotification sbnToPost = trimCache.ForListener(info);
       mHandler.post(new Runnable() {
           @Override
           public void run() {
           //调用NotificationManagerService#notifyPosted
               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);
   }
}

这里 service 是 INotificationListener,对应的是 NotificationListenerWrapper,看 NotificationListenerWrapper#onNotificationPosted。

NotificationListenerWrapper#onNotificationPosted

protected class NotificationListenerWrapper extends INotificationListener.Stub {
   @Override
   public void onNotificationPosted(IStatusBarNotificationHolder sbnHolder,
           NotificationRankingUpdate update) {
       StatusBarNotification sbn;
       try {
           sbn = sbnHolder.get();
       } catch (RemoteException e) {
           Log.w(TAG, "onNotificationPosted: Error receiving StatusBarNotification", e);
           return;
       }
       try {
           // convert icon metadata to legacy format for older clients
           createLegacyIconExtras(sbn.getNotification());
           maybePopulateRemoteViews(sbn.getNotification());
           maybePopulatePeople(sbn.getNotification());
       } catch (IllegalArgumentException e) {
           // warn and drop corrupt notification
           Log.w(TAG, "onNotificationPosted: can't rebuild notification from " +
                   sbn.getPackageName());
           sbn = null;
       }
       // 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();
           } else {
               // still pass along the ranking map, it may contain other information
               mHandler.obtainMessage(MyHandler.MSG_ON_NOTIFICATION_RANKING_UPDATE,
                       mRankingMap).sendToTarget();
           }
       }
   }
}
 

看 MyHandler 处理中的 MSG_ON_NOTIFICATION_POSTED。

MyHandler#handleMessage

private final class MyHandler extends Handler {
   public static final int MSG_ON_NOTIFICATION_POSTED = 1;
   public static final int MSG_ON_NOTIFICATION_REMOVED = 2;
   public static final int MSG_ON_LISTENER_CONNECTED = 3;
   public static final int MSG_ON_NOTIFICATION_RANKING_UPDATE = 4;
   public static final int MSG_ON_LISTENER_HINTS_CHANGED = 5;
   public static final int MSG_ON_INTERRUPTION_FILTER_CHANGED = 6;
   public static final int MSG_ON_NOTIFICATION_CHANNEL_MODIFIED = 7;
   public static final int MSG_ON_NOTIFICATION_CHANNEL_GROUP_MODIFIED = 8;
   public MyHandler(Looper looper) {
       super(looper, null, false);
   }
   @Override
   public void handleMessage(Message msg) {
       if (!isConnected) {
           return;
       }
       switch (msg.what) {
           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;
          //省略其他代码
       }
   }
}

NotificationListenerService#onNotificationPosted

public void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) {
   onNotificationPosted(sbn);
}

NotificationListenerService 是抽象类,NotificationListenerService#onNotificationPosted 在 NotificationListener##onNotificationPosted 有重写。

NotificationListener#onNotificationPosted

@Override
public void onNotificationPosted(final StatusBarNotification sbn,
       final RankingMap rankingMap) {
   if (DEBUG) Log.d(TAG, "onNotificationPosted: " + sbn);
   if (sbn != null && !onPluginNotificationPosted(sbn, rankingMap)) {
       mPresenter.getHandler().post(() -> {
           //省略其他代码
           if (isUpdate) {
               mEntryManager.updateNotification(sbn, rankingMap);
           } else {
               mEntryManager.addNotification(sbn, rankingMap);
           }
       });
   }
}

调用了 NotificationEntryManager#addNotification。

NotificationEntryManager#addNotification

@Override
public void addNotification(StatusBarNotification notification,
       NotificationListenerService.RankingMap ranking) {
   try {
       addNotificationInternal(notification, ranking);
   } catch (InflationException e) {
       handleInflationException(notification, e);
   }
}

private void addNotificationInternal(StatusBarNotification notification,
       NotificationListenerService.RankingMap ranking) throws InflationException {
   String key = notification.getKey();
   if (DEBUG) Log.d(TAG, "addNotification key=" + key);
   mNotificationData.updateRanking(ranking);
   //继续看 createNotificationViews
   NotificationData.Entry shadeEntry = createNotificationViews(notification);
   //省略其他代码
}

protected NotificationData.Entry createNotificationViews(StatusBarNotification sbn)
       throws InflationException {
   if (DEBUG) {
       Log.d(TAG, "createNotificationViews(notification=" + sbn);
   }
   NotificationData.Entry entry = new NotificationData.Entry(sbn);
   Dependency.get(LeakDetector.class).trackInstance(entry);
   entry.createIcons(mContext, sbn);
   // Construct the expanded view.
   inflateViews(entry, mListContainer.getViewParentForNotification(entry));
   return entry;
}

从 NotificationEntryManager#addNotification 到 NotificationEntryManager#addNotificationInternal 再到 NotificationEntryManager#inflateViews 方法。

NotificationEntryManager#inflateViews

private void inflateViews(NotificationData.Entry entry, ViewGroup parent) {
   PackageManager pmUser = StatusBar.getPackageManagerForUser(mContext,
           entry.notification.getUser().getIdentifier());
   final StatusBarNotification sbn = entry.notification;
   if (entry.row != null) {
       entry.reset();
       updateNotification(entry, pmUser, sbn, entry.row);
   } else {
   //来通知会走到这里
       new RowInflaterTask().inflate(mContext, parent, entry,
               row -> {
                   bindRow(entry, pmUser, sbn, row);
                   updateNotification(entry, pmUser, sbn, row);
               });
   }
}

RowInflaterTask#inflate

public void inflate(Context context, ViewGroup parent, NotificationData.Entry entry,
       RowInflationFinishedListener listener) {
   if (TRACE_ORIGIN) {
       mInflateOrigin = new Throwable("inflate requested here");
   }
   mListener = listener;
   AsyncLayoutInflater inflater = new AsyncLayoutInflater(context);
   mEntry = entry;
   entry.setInflationTask(this);
   //这里是Notification布局文件
   inflater.inflate(R.layout.status_bar_notification_row, parent, this);
}

再看 RowInflaterTask#onInflateFinished:

@Override
public void onInflateFinished(View view, int resid, ViewGroup parent) {
   if (!mCancelled) {
       try {
           mEntry.onInflationTaskFinished();
           //1. 调 RowInflationFinishedListener#onInflationFinished
           mListener.onInflationFinished((ExpandableNotificationRow) view);
       } catch (Throwable t) {
           if (mInflateOrigin != null) {
               Log.e(TAG, "Error in inflation finished listener: " + t, mInflateOrigin);
               t.addSuppressed(mInflateOrigin);
           }
           throw t;
       }
   }
}
public interface RowInflationFinishedListener {
//2. 在 NotificationEntryManager#row 实现
   void onInflationFinished(ExpandableNotificationRow row);
}

看 NotificationEntryManager#row 会调用 bindRow 和 updateNotification,看 updateNotification方法最终会调用 ExpandableNotificationRow#updateNotification。

ExpandableNotificationRow#updateNotification

public void updateNotification(NotificationData.Entry entry) {
   mEntry = entry;
   mStatusBarNotification = entry.notification;
   mNotificationInflater.inflateNotificationViews();
   cacheIsSystemNotification();
}

继续跟,到 NotificationInflater#inflateNotificationViews。

NotificationInflater#inflateNotificationViews

@VisibleForTesting
void inflateNotificationViews(int reInflateFlags) {
   if (mRow.isRemoved()) {
      return;
   }
   StatusBarNotification sbn = mRow.getEntry().notification;
   AsyncInflationTask task = new AsyncInflationTask(sbn, reInflateFlags, mRow,
           mIsLowPriority,
           mIsChildInGroup, mUsesIncreasedHeight, mUsesIncreasedHeadsUpHeight, mRedactAmbient,
           mCallback, mRemoteViewClickHandler);
   if (mCallback != null && mCallback.doInflateSynchronous()) {
       task.onPostExecute(task.doInBackground());
   } else {
       task.execute();
   }
}

看 AsyncInflationTask 执行的结果:

public static class AsyncInflationTask extends AsyncTask<Void, Void, InflationProgress>
       implements InflationCallback, InflationTask {
  //省略其他代码
   @Override
   protected InflationProgress doInBackground(Void... params) {
       //省略其他代码
   }
   @Override
   protected void onPostExecute(InflationProgress result) {
       if (mError == null) {
           mCancellationSignal = apply(result, mReInflateFlags, mRow, mRedactAmbient,
                   mRemoteViewClickHandler, this);
       } else {
           handleError(mError);
       }
   }
}

调用 NotificationInflater#apply,最终会到 NotificationInflater#applyRemoteView

@VisibleForTesting
static void applyRemoteView(final InflationProgress result,
       final int reInflateFlags, int inflationId,
       final ExpandableNotificationRow row,
       final boolean redactAmbient, boolean isNewView,
       RemoteViews.OnClickHandler remoteViewClickHandler,
       @Nullable final InflationCallback callback, NotificationData.Entry entry,
       NotificationContentView parentLayout, View existingView,
       NotificationViewWrapper existingWrapper,
       final HashMap<Integer, CancellationSignal> runningInflations,
       ApplyCallback applyCallback) {
   RemoteViews newContentView = applyCallback.getRemoteView();
   //省略其他代码
   RemoteViews.OnViewAppliedListener listener
           = new RemoteViews.OnViewAppliedListener() {
       @Override
       public void onViewApplied(View v) {
           if (isNewView) {
               v.setIsRootNamespace(true);
               applyCallback.setResultView(v);
           } else if (existingWrapper != null) {
               existingWrapper.onReinflated();
           }
           runningInflations.remove(inflationId);
           finishIfDone(result, reInflateFlags, runningInflations, callback, row,
                   redactAmbient);
       }
       //
   };
   CancellationSignal cancellationSignal;
   if (isNewView) {
   //调用RemoteViews#applyAsync,最终回调了上面的onViewApplied方法。
       cancellationSignal = newContentView.applyAsync(
               result.packageContext,
               parentLayout,
               EXECUTOR,
               listener,
               remoteViewClickHandler);
   }//省略其他代码
   runningInflations.put(inflationId, cancellationSignal);
}

继续看 NotificationInflater#finishIfDone,这个方法看到最后的endListener.onAsyncInflationFinished(row.getEntry()); 实现方法在 NotificationEntryManager#onAsyncInflationFinished。

NotificationEntryManager#onAsyncInflationFinished

@Override
public void onAsyncInflationFinished(NotificationData.Entry entry) {
   mPendingNotifications.remove(entry.key);
   // If there was an async task started after the removal, we don't want to add it back to
   // the list, otherwise we might get leaks.
   boolean isNew = mNotificationData.get(entry.key) == null;
   if (isNew && !entry.row.isRemoved()) {
       addEntry(entry);
   } else if (!isNew && entry.row.hasLowPriorityStateUpdated()) {
       mVisualStabilityManager.onLowPriorityUpdated(entry);
       mPresenter.updateNotificationViews();
   }
   entry.row.setLowPriorityStateUpdated(false);
}

这里的 addEntry 方法调用了addNotificationViews,好了,终于和 SystemUI 的通知关联起来了,这样,锁屏来通知分析结束。

Android 9.0 SystemUI Notification

折叠状态栏通知

有了以上锁屏通知分析,再来分析折叠状态栏通知就简单很多了,先看来折叠状态栏初始化部分。

status_bar.xml

折叠状态栏对应的布局文件是 status_bar.xml:

<com.android.systemui.statusbar.AlphaOptimizedFrameLayout
   android:id="@+id/notification_icon_area"
   android:layout_width="0dp"
   android:layout_height="match_parent"
   android:layout_weight="1"
   android:orientation="horizontal"
   android:clipChildren="false"/>

如果来通知,就在 notification_icon_area 进行 addView 填充。再看看代码初始化。

StatusBar#makeStatusBarView

//NotificationIconAreaController 初始化
mNotificationIconAreaController = SystemUIFactory.getInstance()
       .createNotificationIconAreaController(context, this);
inflateShelf();
mNotificationIconAreaController.setupShelf(mNotificationShelf);
mStackScroller.setIconAreaController(mNotificationIconAreaController);
Dependency.get(DarkIconDispatcher.class).addDarkReceiver(mNotificationIconAreaController);
FragmentHostManager.get(mStatusBarWindow)
       .addTagListener(CollapsedStatusBarFragment.TAG, (tag, fragment) -> {
           CollapsedStatusBarFragment statusBarFragment =
                   (CollapsedStatusBarFragment) fragment;
           statusBarFragment.initNotificationIconArea(mNotificationIconAreaController);
           //省略其他代码
       }).getFragmentManager()
       .beginTransaction()
       .replace(R.id.status_bar_container, new CollapsedStatusBarFragment(),
               CollapsedStatusBarFragment.TAG)
       .commit();

CollapsedStatusBarFragment#initNotificationIconArea

public void initNotificationIconArea(NotificationIconAreaController
       notificationIconAreaController) {
   ViewGroup notificationIconArea = mStatusBar.findViewById(R.id.notification_icon_area);
   mNotificationIconAreaInner =
           notificationIconAreaController.getNotificationInnerAreaView();
   if (mNotificationIconAreaInner.getParent() != null) {
       ((ViewGroup) mNotificationIconAreaInner.getParent())
               .removeView(mNotificationIconAreaInner);
   }
   notificationIconArea.addView(mNotificationIconAreaInner);
   // Default to showing until we know otherwise.
   showNotificationIconArea(false);
}

看到这里的notificationIconArea.addView(mNotificationIconAreaInner); ,notificationIconArea 被 mNotificationIconAreaInner 填充,因此我们要重点关注 NotificationIconAreaController 什么时候被填充。

有以上锁屏通知分析知道有通知来最后会调用 StatusBar#updateNotificationViews。

StatusBar#updateNotificationViews

@Override
public void updateNotificationViews() {
   // 省略其他代码
   mNotificationIconAreaController.updateNotificationIcons();
}

调用 NotificationIconAreaController#updateNotificationIcons。

NotificationIconAreaController#updateNotificationIcons

public void updateNotificationIcons() {
   updateStatusBarIcons();
   updateShelfIcons();
   updateHasShelfIconsWhenFullyDark();
   applyNotificationIconsTint();
}

public void updateStatusBarIcons() {
   updateIconsForLayout(entry -> entry.icon, mNotificationIcons,
           false /* showAmbient */, true /* hideDismissed */, true /* hideRepliedMessages */);
}

private void updateIconsForLayout(Function<NotificationData.Entry, StatusBarIconView> function,
       NotificationIconContainer hostLayout, boolean showAmbient, boolean hideDismissed,
       boolean hideRepliedMessages) {
   ArrayList<StatusBarIconView> toShow = new ArrayList<>(
           mNotificationScrollLayout.getChildCount());
   //省略其他代码
   final FrameLayout.LayoutParams params = generateIconLayoutParams();
   for (int i = 0; i < toShow.size(); i++) {
       StatusBarIconView v = toShow.get(i);
       // The view might still be transiently added if it was just removed and added again
       hostLayout.removeTransientView(v);
       if (v.getParent() == null) {
           if (hideDismissed) {
               v.setOnDismissListener(mUpdateStatusBarIcons);
           }
           hostLayout.addView(v, i, params);
       }
   }
   hostLayout.setChangingViewPositions(true);
   // Re-sort notification icons
   final int childCount = hostLayout.getChildCount();
   for (int i = 0; i < childCount; i++) {
       View actual = hostLayout.getChildAt(i);
       StatusBarIconView expected = toShow.get(i);
       if (actual == expected) {
           continue;
       }
       hostLayout.removeView(expected);
       hostLayout.addView(expected, i);
   }
   hostLayout.setChangingViewPositions(false);
   hostLayout.setReplacingIcons(null);
}

OK,折叠状态栏通知分析结束。

结语

本篇梳理了 SystemUI Notification 大致流程,分为锁屏的通知和状态栏通知,代码很多,细节没有去纠结,省略了很多代码,有兴趣,可以自己去 AOSP 查看。


上一篇:依赖反转原则


下一篇:H5的Notification特性 - Web的桌面通知功能