3

Android 9.0 SystemUI Notification

 3 years ago
source link: http://wuxiaolong.me/2019/08/12/SystemUI4/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

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

StatusBar5.png

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

StatusBar6.png
序列图为来通知到 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);
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) {
// 省略其他代码
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);
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;
sbn = sbnHolder.get();
} catch (RemoteException e) {
Log.w(TAG, "onNotificationPosted: Error receiving StatusBarNotification", e);
return;
// 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) {
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) {
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 查看。

 我的微信公众号:吴小龙同学,欢迎关注交流,公号回复关键字「1024」有惊喜哦。
code?username=MrWuXiaolong

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK