专栏首页吴小龙同學Android 9.0 SystemUI Notification

Android 9.0 SystemUI Notification

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

概述

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

序列图

序列图为来通知到 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 的通知关联起来了,这样,锁屏来通知分析结束。

折叠状态栏通知

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

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 查看。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Android 9.0 SystemUI NavigationBar

    导航栏有返回(back),桌面(home),最近任务(recent),本篇主要学习这三个是如何加载的,点击事件在哪里写的?基于 AOSP 9.0 分析。

    吴小龙同學
  • Android 亮度自动调节

    下拉状态栏有个亮度的进度条,如果开启了亮度自动调节开关,会随着周围光线变化,这个进度条也会随着变化,接下来就是看看这个功能是如何实现的。

    吴小龙同學
  • Android 9.0 SystemUI 主要视图 SystemBars

    SystemBars 是 SystemUI 主要视图,接下来看看它是如何呈现?基于 AOSP 9.0 分析。

    吴小龙同學
  • Vue 3.0 源码分析-数据侦测

    2020年前端大事件之一,Vue 3.0终于正式发布了。作为一个大的版本更新,Vue 3 与 Vue 2相比,实现原理,使用方式等均有着不小的改动。本文主要会...

    QQ音乐前端团队
  • GATK4的gvcf流程

    得到了它们的bam文件,也是走的GATK流程,这里就不多说了。本教程首发于生信技能树VIP论坛:https://vip.biotrainee.com/d/423...

    生信技能树
  • 1018. 锤子剪刀布 (20)

    现给出两人的交锋记录,请统计双方的胜、平、负次数,并且给出双方分别出什么手势的胜算最大。

    AI那点小事
  • leetcode-151-翻转字符串里的单词

    1、这道题给定一个字符串s,相当于一个英文句子,要求把这个句子中的单词反转一下,后面的要放在前面,前面放在后面。

    chenjx85
  • 米斯特白帽培训讲义 漏洞篇 越权

    越权漏洞是 Web 应用程序中一种常见的安全漏洞。它的威胁在于一个账户即可控制全站用户数据。当然这些数据仅限于存在漏洞功能对应的数据。越权漏洞的成因主要是开发人...

    ApacheCN_飞龙
  • LeetCode - Fizz Buzz

    原题地址:https://leetcode-cn.com/problems/fizz-buzz/

    晓痴
  • Golang Leetcode 819. Most Common Word.go

    用map保存所有的banned词出现的数量,处理paragraph之后,从map中获取是否出现过,然后找出最大值

    anakinsun

扫码关注云+社区

领取腾讯云代金券

,,