SystemUI中的Notification流程

不管是发出一个新的通知还是对已经存在的通知进行更新,调用的都是NotificationManager.notify(int id,Notification notification)。最后走到SystemUI的时候首先调用StatusBar中的成员变量mNotificationListener的onNotificationPosted(final StatusBarNotification sbn, final RankingMap rankingMap)方法。成员变量mNotificationListener是一个NotificationListenerWithPlugins类的对象,NotificationListenerWithPlugins是NotificationListenerService的父类,NotificationListenerService不在SystemUI中,在android.service.notification中,暂不分析.

来看mNotificationListener对onNotificationPosted(final StatusBarNotification sbn,final RankingMap rankingMap)方法的实现。

public void onNotificationPosted(final StatusBarNotification sbn,
        final RankingMap rankingMap) {
    if (sbn != null && !onPluginNotificationPosted(sbn, rankingMap)) {
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                processForRemoteInput(sbn.getNotification());
                String key = sbn.getKey();
                mKeysKeptForRemoteInput.remove(key);
                boolean isUpdate = mNotificationData.get(key) != null;
                ......
                    // Remove existing notification to avoid stale data.
                    if (isUpdate) {
                        removeNotification(key, rankingMap);
                    } else {
                        mNotificationData.updateRanking(rankingMap);
                    }
                    return;
                }
                try {
                    if (isUpdate) {
                        updateNotification(sbn, rankingMap);
                    } else {
                        addNotification(sbn, rankingMap);
                    }
                } catch (InflationException e) {
                    handleInflationException(sbn, e);
                }
            }
        });
    }
}

首先来看方法中的两个参数:1.StatusBarNotification sbn;2.RankingMap rankingMap。
StatusBarNotification点进去看,发现其实是由Notification组装而成,里面比较重要的属性有String pkg,int id,String key,Notification notification,保存着通知的内容,发出通知的报名信息,以及id等。StatusBarNotification 具体的组装生成过程不是在SystemUI包中进行,暂不关注。

RankingMap则是NotificationListenerService的一个静态内部类,里面保存着所有Notification相关的信息,具体的我们先往下看。

在onNotificationPosted()方法中,先对传过来的sbn进行空判断,不为空,则用mHandler发一个新的runnable来处理

String key = sbn.getKey();

mKeysKeptForRemoteInput.remove(key);

boolean isUpdate = mNotificationData.get(key) != null;

先拿到sbn里面key属性,再根据这个key去mNotificationData取对象。
mNotificationData是StatusBar的一个protected成员变量,可被子类继承,自己本身的类是NotificationData,位于SystemUI工程下的com.android.systemui.statusbar。它的get(key)方法如下:

public Entry get(String key) {
    return mEntries.get(key);
}

返回了一个Entry对象mEntries.get(key),我们来看看这个Entry是什么。Entry是NotificationData的一个内部类。其中包含的几个重要的属性的属性:

public String key;
public StatusBarNotification notification;
public NotificationChannel channel;
public StatusBarIconView icon;
public StatusBarIconView expandedIcon;
public ExpandableNotificationRow row; // the outer expanded view

显然,我们的Notification先被封装成StatusBarNotification传到SystemUI,然后在SystemUI中又再一次被封装进这个Entry中。

然后我们再看看这个mEntries,这是NotificationData的一个成员变量:

private final ArrayMap<String, Entry> mEntries = new ArrayMap<>();

暂时没看到这个map的数据是在哪里添加的。
我们回到onNotificationPosted方法,跳过一部分代码,直接来到重点。

if (isUpdate) {
    updateNotification(sbn, rankingMap);
} else {
    addNotification(sbn, rankingMap);
}

如果mNotificationData能通过sbn的key拿到的Entry不为空,说明这个通知已经存在了,isUpdate为true走更新流程,否则走添加流程。到此,onNotificationPosted方法就结束了。

我们先来看添加流程addNotification(sbn, rankingMap)。
在StatusBar中,这个方法是被这么实现的

public void addNotification(StatusBarNotification notification, RankingMap ranking)
        throws InflationException {
    String key = notification.getKey();
    mNotificationData.updateRanking(ranking);
    Entry shadeEntry = createNotificationViews(notification);
    boolean isHeadsUped = shouldPeek(shadeEntry);
    ......
    abortExistingInflation(key);
 
    mForegroundServiceController.addNotification(notification,
            mNotificationData.getImportance(key));
 
    mPendingNotifications.put(key, shadeEntry);
}

首先通过传来的StatusBarNotification notification封装构造出一个Entry对象(注意,这的notification是StabusBarNotification不是Notification,也就是onNotificationPosted传入的sbn)

Entry shadeEntry = createNotificationViews(notification);

跟过去看createNotificationViews(notification)方法,这里又跳回了StatusBar。

protected NotificationData.Entry createNotificationViews(StatusBarNotification sbn)
        throws InflationException {
    NotificationData.Entry entry = new NotificationData.Entry(sbn);
    Dependency.get(LeakDetector.class).trackInstance(entry);
    entry.createIcons(mContext, sbn);
    // Construct the expanded view.
    inflateViews(entry, mStackScroller);
    return entry;
}

这里面的重点是inflateViews(entry, mStackScroller)。第二个参数mStackScroller,就是SystemUI中的下拉通知栏里面所有通知以及一些其他view的父view,是StatusBar中一个成员变量。
跟过去看方法细节

protected void inflateViews(Entry entry, ViewGroup parent) {
    PackageManager pmUser = 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方法,该方法在RowInflaterTask中

public void inflate(Context context, ViewGroup parent, NotificationData.Entry entry,
        RowInflationFinishedListener listener) {
    mListener = listener;
    AsyncLayoutInflater inflater = new AsyncLayoutInflater(context);
    mEntry = entry;
    entry.setInflationTask(this);
    inflater.inflate(R.layout.status_bar_notification_row, parent, this);
}

这个row(ExpandableNotificationRow)就是最终添加到通知栏上的通知对应的view,它的布局文件是R.layout.status_bar_notification_row。

这里重点说一下AsyncLayoutInflater这个类,它是NotificationInflater的静态内部类,其中有方法onAsyncInflationFinished如下

public void onAsyncInflationFinished(NotificationData.Entry entry) {
    mRow.getEntry().onInflationTaskFinished();
    mRow.onNotificationUpdated();
    mCallback.onAsyncInflationFinished(mRow.getEntry());
}
onAsyncInflationFinished的实现在StatusBar中,如下
public void onAsyncInflationFinished(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);
        updateNotificationShade();
    }
    entry.row.setLowPriorityStateUpdated(false);
}

addEntry是重点,如下

private void addEntry(Entry shadeEntry) {
    boolean isHeadsUped = shouldPeek(shadeEntry);
    if (isHeadsUped) {
        mHeadsUpManager.showNotification(shadeEntry);
        // Mark as seen immediately
        setNotificationShown(shadeEntry.notification);
    }
    addNotificationViews(shadeEntry);//重点
    // Recalculate the position of the sliding windows and the titles.
    setAreThereNotifications();
}

addNotificationViews的内容如下

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

mNotificationData.add(entry);对应了前面分析的boolean isUpdate = mNotificationData.get(key) != null;

进入updateNotifications()方法,内容如下

protected void updateNotifications() {
    mNotificationData.filterAndSort();
 
    updateNotificationShade();
}

重点在updateNotificationShade()(实际上,我就是在这个方法里进行了一些修改达到我想要的需求)

private void updateNotificationShade() {
    if (mStackScroller == null) return;
 
    // Do not modify the notifications during collapse.
    if (isCollapsing()) {
        addPostCollapseAction(new Runnable() {
            @Override
            public void run() {
                updateNotificationShade();
            }
        });
        return;
    }
 
    ArrayList<Entry> activeNotifications = mNotificationData.getActiveNotifications();
    ArrayList<ExpandableNotificationRow> toShow = new ArrayList<>(activeNotifications.size());
    final int N = activeNotifications.size();
    for (int i=0; i<N; i++) {
        Entry ent = activeNotifications.get(i);
        if (ent.row.isDismissed() || ent.row.isRemoved()) {
            // we don't want to update removed notifications because they could
            // temporarily become children if they were isolated before.
            continue;
        }
        int userId = ent.notification.getUserId();
 
        // Display public version of the notification if we need to redact.
        boolean devicePublic = isLockscreenPublicMode(mCurrentUserId);
        boolean userPublic = devicePublic || isLockscreenPublicMode(userId);
        boolean needsRedaction = needsRedaction(ent);
        boolean sensitive = userPublic && needsRedaction;
        boolean deviceSensitive = devicePublic
                && !userAllowsPrivateNotificationsInPublic(mCurrentUserId);
        ent.row.setSensitive(sensitive, deviceSensitive);
        ent.row.setNeedsRedaction(needsRedaction);
        if (mGroupManager.isChildInGroupWithSummary(ent.row.getStatusBarNotification())) {
            ExpandableNotificationRow summary = mGroupManager.getGroupSummary(
                    ent.row.getStatusBarNotification());
            List<ExpandableNotificationRow> orderedChildren =
                    mTmpChildOrderMap.get(summary);
            if (orderedChildren == null) {
                orderedChildren = new ArrayList<>();
                mTmpChildOrderMap.put(summary, orderedChildren);
            }
            orderedChildren.add(ent.row);
        } else {
            toShow.add(ent.row);
        }
 
    }
 
    ArrayList<ExpandableNotificationRow> toRemove = new ArrayList<>();
    for (int i=0; i< mStackScroller.getChildCount(); i++) {
        View child = mStackScroller.getChildAt(i);
        if (!toShow.contains(child) && child instanceof ExpandableNotificationRow) {
            toRemove.add((ExpandableNotificationRow) child);
        }
    }
 
    for (ExpandableNotificationRow remove : toRemove) {
        if (mGroupManager.isChildInGroupWithSummary(remove.getStatusBarNotification())) {
            // we are only transfering this notification to its parent, don't generate an animation
            mStackScroller.setChildTransferInProgress(true);
        }
        if (remove.isSummaryWithChildren()) {
            remove.removeAllChildren();
        }
        mStackScroller.removeView(remove);
        mStackScroller.setChildTransferInProgress(false);
    }
 
    removeNotificationChildren();
 
    for (int i=0; i<toShow.size(); i++) {
        View v = toShow.get(i);
        if (v.getParent() == null) {
            //*/ freeme.biantao, 20180207. Theme.Freeme 8.1 - notification.
            if (v instanceof ExpandableNotificationRow) {
                ExpandableNotificationRow row = (ExpandableNotificationRow) v;
                if (row.isStyleInGroup()) {
                    row.setStyleInGroup(false);
                }
            }
            //*/
            mVisualStabilityManager.notifyViewAddition(v);
            mStackScroller.addView(v);
        }
    }
 
    addNotificationChildrenAndSort();
 
    // So after all this work notifications still aren't sorted correctly.
    // Let's do that now by advancing through toShow and mStackScroller in
    // lock-step, making sure mStackScroller matches what we see in toShow.
    int j = 0;
    for (int i = 0; i < mStackScroller.getChildCount(); i++) {
        View child = mStackScroller.getChildAt(i);
        if (!(child instanceof ExpandableNotificationRow)) {
            // We don't care about non-notification views.
            continue;
        }
 
        ExpandableNotificationRow targetChild = toShow.get(j);
        if (child != targetChild) {
            // Oops, wrong notification at this position. Put the right one
            // here and advance both lists.
            if (mVisualStabilityManager.canReorderNotification(targetChild)) {
                mStackScroller.changeViewPosition(targetChild, i);
            } else {
                mVisualStabilityManager.addReorderingAllowedCallback(this);
            }
        }
        j++;
 
    }
 
    mVisualStabilityManager.onReorderingFinished();
    // clear the map again for the next usage
    mTmpChildOrderMap.clear();
 
    updateRowStates();
    updateSpeedBumpIndex();
    updateClearAll();
    updateEmptyShadeView();
 
    updateQsExpansionEnabled();
 
    // Let's also update the icons
    mNotificationIconAreaController.updateNotificationIcons(mNotificationData);
}

显示做mStackScroller的空判断,然后是通知栏动画状态的判断。一切OK,就:
1.mNotificationData获取数据Entry集合,构造一个大小和这个Entry集合一样的ExpandableNotificationRow集合toShow
2.遍历entry,把entry.row添加到toshow里面
3.原有的通知,但是toshow里没有的则移除,然后toshow里没添加上的添加上去

至此我们的通知就成功地添加到通知栏了。

看bindRow(entry, pmUser, sbn, row)方法

private void bindRow(Entry entry, PackageManager pmUser,
        StatusBarNotification sbn, ExpandableNotificationRow row) {
    row.setExpansionLogger(this, entry.notification.getKey());
    row.setGroupManager(mGroupManager);
    row.setHeadsUpManager(mHeadsUpManager);
    row.setAboveShelfChangedListener(mAboveShelfObserver);
    row.setRemoteInputController(mRemoteInputController);
    row.setOnExpandClickListener(this);
    row.setRemoteViewClickHandler(mOnClickHandler);
    row.setInflationCallback(this);
    row.setSecureStateProvider(this::isKeyguardCurrentlySecure);
    final String pkg = sbn.getPackageName();
    String appname = pkg;
    try {
        final ApplicationInfo info = pmUser.getApplicationInfo(pkg,
                PackageManager.MATCH_UNINSTALLED_PACKAGES
                        | PackageManager.MATCH_DISABLED_COMPONENTS);
        if (info != null) {
            appname = String.valueOf(pmUser.getApplicationLabel(info));
        }
    } catch (NameNotFoundException e) {
        // Do nothing
    }
    row.setAppName(appname);
    row.setOnDismissRunnable(() ->
            performRemoveNotification(row.getStatusBarNotification()));
    row.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
//该句实现了焦点的策略,row(也就是我们的通知)有子控件需要焦点则把焦点交给子控件,否则给row。
    if (ENABLE_REMOTE_INPUT) {
        row.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
    }
}

看updateNotification(entry,pmUser,sbn,entry.row)方法

private void updateNotification(Entry entry, PackageManager pmUser,
        StatusBarNotification sbn, ExpandableNotificationRow row) {
    row.setNeedsRedaction(needsRedaction(entry));
    boolean isLowPriority = mNotificationData.isAmbient(sbn.getKey());
    boolean isUpdate = mNotificationData.get(entry.key) != null;
    boolean wasLowPriority = row.isLowPriority();
    row.setIsLowPriority(isLowPriority);
    row.setLowPriorityStateUpdated(isUpdate && (wasLowPriority != isLowPriority));
    // bind the click event to the content area
    mNotificationClicker.register(row, sbn);//给通知栏注册点击事件
 
    // Extract target SDK version.
    try {
        ApplicationInfo info = pmUser.getApplicationInfo(sbn.getPackageName(), 0);
        entry.targetSdk = info.targetSdkVersion;
    } catch (NameNotFoundException ex) {
        Log.e(TAG, "Failed looking up ApplicationInfo for " + sbn.getPackageName(), ex);
    }
    row.setLegacy(entry.targetSdk >= Build.VERSION_CODES.GINGERBREAD
            && entry.targetSdk < Build.VERSION_CODES.LOLLIPOP);
    entry.setIconTag(R.id.icon_is_pre_L, entry.targetSdk < Build.VERSION_CODES.LOLLIPOP);
    entry.autoRedacted = entry.notification.getNotification().publicVersion == null;
 
    entry.row = row;
    entry.row.setOnActivatedListener(this);
 
    boolean useIncreasedCollapsedHeight = mMessagingUtil.isImportantMessaging(sbn,
            mNotificationData.getImportance(sbn.getKey()));
    boolean useIncreasedHeadsUp = useIncreasedCollapsedHeight && mPanelExpanded;
    row.setUseIncreasedCollapsedHeight(useIncreasedCollapsedHeight);
    row.setUseIncreasedHeadsUpHeight(useIncreasedHeadsUp);
    row.updateNotification(entry);
}

mNotificationClicker.register(row, sbn);mNotificationClicker是StatusBar的一个私有成员,对应的类是NotificationClicker,是一个StatusBar的内部类,实现了View.OnClickListener接口,它就是row的监听类,实现的功能是row被点击时,启动相应的pendingIntent.注册操作代码如下

public void register(ExpandableNotificationRow row, StatusBarNotification sbn) {
    Notification notification = sbn.getNotification();
    if (notification.contentIntent != null || notification.fullScreenIntent != null) {
        row.setOnClickListener(this);
    } else {
        row.setOnClickListener(null);
    }
}

进入ExpandableNotificationRow的updateNotification方法

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

至此inflateViews()结束,Entry生成完毕,Entry中的row生成完毕。

Notification的更新

接着回到最开始的updateNotification(StatusBarNotification notification, RankingMap ranking)方法中

public void updateNotification(StatusBarNotification notification, RankingMap ranking)
        throws InflationException {
    if (DEBUG) Log.d(TAG, "updateNotification(" + notification + ")");
 
    final String key = notification.getKey();
    abortExistingInflation(key);
    Entry entry = mNotificationData.get(key);
    if (entry == null) {
        return;
    }
    mHeadsUpEntriesToRemoveOnSwitch.remove(entry);
    mRemoteInputEntriesToRemoveOnCollapse.remove(entry);
    if (key.equals(mKeyToRemoveOnGutsClosed)) {
        mKeyToRemoveOnGutsClosed = null;
        Log.w(TAG, "Notification that was kept for guts was updated. " + key);
    }
 
    Notification n = notification.getNotification();
    mNotificationData.updateRanking(ranking);
 
    final StatusBarNotification oldNotification = entry.notification;
    entry.notification = notification;
    mGroupManager.onEntryUpdated(entry, oldNotification);
 
    entry.updateIcons(mContext, notification);
    inflateViews(entry, mStackScroller);
 
    mForegroundServiceController.updateNotification(notification,
            mNotificationData.getImportance(key));
 
    boolean shouldPeek = shouldPeek(entry, notification);
    boolean alertAgain = alertAgain(entry, n);
 
    updateHeadsUp(key, entry, shouldPeek, alertAgain);
    updateNotifications();
 
    if (!notification.isClearable()) {
        // The user may have performed a dismiss action on the notification, since it's
        // not clearable we should snap it back.
        mStackScroller.snapViewIfNeeded(entry.row);
    }
 
    if (DEBUG) {
        // Is this for you?
        boolean isForCurrentUser = isNotificationForCurrentProfiles(notification);
        Log.d(TAG, "notification is " + (isForCurrentUser ? "" : "not ") + "for you");
    }
 
    setAreThereNotifications();
}

更新Notification走的还是inflateViews方法和updateNotifications方法

移除通知

首先还是StatusBar中的mNotificationListener但是和notification的添加/更新不同的是,走的不再是onNotificationPosted方法,而是onNotificationRemoved

public void onNotificationRemoved(StatusBarNotification sbn,
        final RankingMap rankingMap) {
    if (true/**DEBUG*/) Log.d(TAG, "onNotificationRemoved: " + sbn);
    if (sbn != null && !onPluginNotificationRemoved(sbn, rankingMap)) {
        final String key = sbn.getKey();
        mHandler.post(() -> removeNotification(key, rankingMap));
    }
}

这个方法就是很简单地拿个key,然后走removeNotification(key, rankingMap)方法.

public void removeNotification(String key, RankingMap ranking) {
    boolean deferRemoval = false;
    abortExistingInflation(key);
    if (mHeadsUpManager.isHeadsUp(key)) {
        // A cancel() in repsonse to a remote input shouldn't be delayed, as it makes the
        // sending look longer than it takes.
        // Also we should not defer the removal if reordering isn't allowed since otherwise
        // some notifications can't disappear before the panel is closed.
        boolean ignoreEarliestRemovalTime = mRemoteInputController.isSpinning(key)
                && !FORCE_REMOTE_INPUT_HISTORY
                || !mVisualStabilityManager.isReorderingAllowed();
        deferRemoval = !mHeadsUpManager.removeNotification(key,  ignoreEarliestRemovalTime);
    }
    if (key.equals(mMediaNotificationKey)) {
        clearCurrentMediaNotification();
        updateMediaMetaData(true, true);
    }
    if (FORCE_REMOTE_INPUT_HISTORY && mRemoteInputController.isSpinning(key)) {
        Entry entry = mNotificationData.get(key);
        StatusBarNotification sbn = entry.notification;
 
        Notification.Builder b = Notification.Builder
                .recoverBuilder(mContext, sbn.getNotification().clone());
        CharSequence[] oldHistory = sbn.getNotification().extras
                .getCharSequenceArray(Notification.EXTRA_REMOTE_INPUT_HISTORY);
        CharSequence[] newHistory;
        if (oldHistory == null) {
            newHistory = new CharSequence[1];
        } else {
            newHistory = new CharSequence[oldHistory.length + 1];
            for (int i = 0; i < oldHistory.length; i++) {
                newHistory[i + 1] = oldHistory[i];
            }
        }
        newHistory[0] = String.valueOf(entry.remoteInputText);
        b.setRemoteInputHistory(newHistory);
 
        Notification newNotification = b.build();
 
        // Undo any compatibility view inflation
        newNotification.contentView = sbn.getNotification().contentView;
        newNotification.bigContentView = sbn.getNotification().bigContentView;
        newNotification.headsUpContentView = sbn.getNotification().headsUpContentView;
 
        StatusBarNotification newSbn = new StatusBarNotification(sbn.getPackageName(),
                sbn.getOpPkg(),
                sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(),
                newNotification, sbn.getUser(), sbn.getOverrideGroupKey(), sbn.getPostTime());
        boolean updated = false;
        try {
            updateNotification(newSbn, null);
            updated = true;
        } catch (InflationException e) {
            deferRemoval = false;
        }
        if (updated) {
            mKeysKeptForRemoteInput.add(entry.key);
            return;
        }
    }
    if (deferRemoval) {
        mLatestRankingMap = ranking;
        mHeadsUpEntriesToRemoveOnSwitch.add(mHeadsUpManager.getEntry(key));
        return;
    }
    Entry entry = mNotificationData.get(key);
 
    if (entry != null && mRemoteInputController.isRemoteInputActive(entry)
            && (entry.row != null && !entry.row.isDismissed())) {
        mLatestRankingMap = ranking;
        mRemoteInputEntriesToRemoveOnCollapse.add(entry);
        return;
    }
    if (entry != null && mNotificationGutsExposed != null
            && mNotificationGutsExposed == entry.row.getGuts() && entry.row.getGuts() != null
            && !entry.row.getGuts().isLeavebehind()) {
        Log.w(TAG, "Keeping notification because it's showing guts. " + key);
        mLatestRankingMap = ranking;
        mKeyToRemoveOnGutsClosed = key;
        return;
    }
 
    if (entry != null) {
        mForegroundServiceController.removeNotification(entry.notification);
    }
 
    if (entry != null && entry.row != null) {
        entry.row.setRemoved();
        mStackScroller.cleanUpViewState(entry.row);
    }
    // Let's remove the children if this was a summary
    handleGroupSummaryRemoved(key, ranking);
    StatusBarNotification old = removeNotificationViews(key, ranking);
    /// M: Enable this log for unusual case debug.
    if (true/**SPEW*/) Log.d(TAG, "removeNotification key=" + key + " old=" + old);
 
    if (old != null) {
        if (CLOSE_PANEL_WHEN_EMPTIED && !hasActiveNotifications()
                && !mNotificationPanel.isTracking() && !mNotificationPanel.isQsExpanded()) {
            if (mState == StatusBarState.SHADE) {
                animateCollapsePanels();
            } else if (mState == StatusBarState.SHADE_LOCKED && !isCollapsing()) {
                goToKeyguard();
            }
        }
    }
    setAreThereNotifications();
}

这里面的重点是removeNotificationViews(key, ranking)方法,这个方法是在StatusBar中定义的

protected StatusBarNotification removeNotificationViews(String key, RankingMap ranking) {
    NotificationData.Entry entry = mNotificationData.remove(key, ranking);
    if (entry == null) {
        Log.w(TAG, "removeNotification for unknown key: " + key);
        return null;
    }
    updateNotifications();
    Dependency.get(LeakDetector.class).trackGarbage(entry);
    return entry.notification;
}

里面逻辑也很简单,根据key,从mNotificationData移除entry,然后就是走回我们熟悉的updateNotifications()刷新UI。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容