SystemUI之狀態列notification icon載入流程
引言
今天我們主要講的是SystemUI狀態列裡面另一個常見的icons——notification icons,該icons主要用於顯示app或者framework傳送的各種notification icon,表示當前有新的通知來了,需要下拉通知欄進行檢視,以達到提示使用者的目的。
正文
本文主要從兩個方面講述下notification icon功能,主要分為初始化流程和通知icon顯示流程
話不多說,我們開始吧。
初始化流程
首先我們看下狀態列的佈局檔案 status_bar.xml
<!-- The alpha of this area is controlled from both PhoneStatusBarTransitions and PhoneStatusBar (DISABLE_NOTIFICATION_ICONS). --> <com.android.systemui.statusbar.AlphaOptimizedFrameLayout android:id="@+id/notification_icon_area" android:layout_width="0dip" android:layout_height="match_parent" android:layout_weight="1" android:orientation="horizontal" /> <com.android.keyguard.AlphaOptimizedLinearLayout android:id="@+id/system_icon_area" android:layout_width="wrap_content" android:layout_height="match_parent" android:orientation="horizontal" >
我們今天講的notification icons就是這個 android:id="@+id/notification_icon_area" 了。
同樣它最外層是一個AlphaOptimizedFrameLayout控制元件,前文已經說過類似的了,
SystemUI之狀態列status icon載入流程
該控制元件實現了hasOverlappingRendering()方法,該方法用來標記當前view是否存在過度繪製。
接下來我們看下SystemUI是怎麼載入這個AlphaOptimizedFrameLayout
CollapsedStatusBarFragment.java
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); }
NotificationIconAreaController.java
/** * Initializes the views that will represent the notification area. */ protected void initializeNotificationAreaViews(Context context) { reloadDimens(context); LayoutInflater layoutInflater = LayoutInflater.from(context); mNotificationIconArea = inflateIconArea(layoutInflater); mNotificationIcons = (NotificationIconContainer) mNotificationIconArea.findViewById( R.id.notificationIcons); mNotificationScrollLayout = mStatusBar.getNotificationScrollLayout(); } protected View inflateIconArea(LayoutInflater inflater) { return inflater.inflate(R.layout.notification_icon_area, null); }
如上,我們就找到了初始化的地方,主要是通過inflate這個R.layout.notification_icon_area檔案,通過addView的方式新增到了AlphaOptimizedFrameLayout,下面就是看下R.layout.notification_icon_area這個檔案了
<com.android.keyguard.AlphaOptimizedLinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/notification_icon_area_inner"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<com.android.systemui.statusbar.phone.NotificationIconContainer
android:id="@+id/notificationIcons"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentStart="true"
android:gravity="center_vertical"
android:orientation="horizontal"/>
</com.android.keyguard.AlphaOptimizedLinearLayout>
/**
* A container for notification icons. It handles overflowing icons properly and positions them
* correctly on the screen.
*/
public class NotificationIconContainer extends AlphaOptimizedFrameLayout {}
從備註就能夠看出來,這個就是所有notification icons的父控制元件,所有icons最後都是新增到這裡面來的,
好了,到這裡我們的第一部分初始化流程就講完了。
通知icon顯示流程
首先我們需要看下notification生成的地方
StatusBar.java
public void onNotificationPosted(final StatusBarNotification sbn,
final RankingMap rankingMap) {
if (isUpdate) {
updateNotification(sbn, rankingMap);
} else {
addNotification(sbn, rankingMap);
}
}
public void addNotification(StatusBarNotification notification, RankingMap ranking)
throws InflationException {
String key = notification.getKey();
if (DEBUG) Log.d(TAG, "addNotification key=" + key);
mNotificationData.updateRanking(ranking);
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, mStackScroller);
return entry;
}
}
層層呼叫之後,最後會通過entry.createIcons(mContext, sbn)生成notification icon,然後存放在NotificationData裡面,感興趣的可以看下entry.createIcons(mContext, sbn), 該函式裡面主要生成了一個StatusBarIconView物件,這個就是最終顯示在狀態列的icon。
notification生成的過程大致就是通過inflateViews(entry, mStackScroller)--->AsyncInflationTask處理載入然後生成ExpandableNotificationRow等的資訊,最後通過StatusBar.java
@Override
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);
}
private void updateNotificationShade() {
................................................
for (int i=0; i<toShow.size(); i++) {
View v = toShow.get(i);
if (v.getParent() == null) {
mVisualStabilityManager.notifyViewAddition(v);
mStackScroller.addView(v);
}
}
................................................
// Let's also update the icons
mNotificationIconAreaController.updateNotificationIcons(mNotificationData);//新增notification icons
}
updateNotificationShade這個函式回撥完成view的新增,這個函式先是把inflate出來的通知,新增到NotificationScrollLayout裡面,然後再新增notification icon,接下來我們就看下updateNotificationIcons裡面的邏輯了。
NotificationIconAreaController.java
private NotificationIconContainer mNotificationIcons;
/**
* Updates the notifications with the given list of notifications to display.
*/
public void updateNotificationIcons(NotificationData notificationData) {
updateIconsForLayout(notificationData, entry -> entry.icon, mNotificationIcons,
false /* showAmbient */);// 新增status bar notification icon
updateIconsForLayout(notificationData, entry -> entry.expandedIcon, mShelfIcons,
NotificationShelf.SHOW_AMBIENT_ICONS);// 新增 notification self icon
applyNotificationIconsTint();
}
主要的新增動作就在updateIconsForLayout這個函式中了
private void updateIconsForLayout(NotificationData notificationData,
Function<NotificationData.Entry, StatusBarIconView> function,
NotificationIconContainer hostLayout, boolean showAmbient) {
ArrayList<StatusBarIconView> toShow = new ArrayList<>(
mNotificationScrollLayout.getChildCount());
// Filter out ambient notifications and notification children.
for (int i = 0; i < mNotificationScrollLayout.getChildCount(); i++) {
View view = mNotificationScrollLayout.getChildAt(i);
if (view instanceof ExpandableNotificationRow) {
NotificationData.Entry ent = ((ExpandableNotificationRow) view).getEntry();
if (shouldShowNotificationIcon(ent, notificationData, showAmbient)) {
toShow.add(function.apply(ent));
}
}
}
......................................
......................................
......................................
final FrameLayout.LayoutParams params = generateIconLayoutParams();
for (int i = 0; i < toShow.size(); i++) {
View 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) {
hostLayout.addView(v, i, params);
}
}
}
首先從mNotificationScrollLayout取出NotificationData,然後把NotificationData存放的StatusBarIconView取出新增到toShow裡面,最後遍歷新增到NotificationIconContainer中,這樣就完成了往NotificationIconContainer新增icon的過程。
到這裡,notification icon載入流程已經講完,後面有時間還會講下signal icon的載入流程,敬請關