學(xué)習(xí)筆記:參考資源 https://zhuanlan.zhihu.com/p/142596265学赛、https://blog.csdn.net/Bill_xiao/article/details/108244267
一港华、StatusBar簡(jiǎn)介
Statusbar包含導(dǎo)航欄(NavigationBar, 位于左側(cè)寺枉、右側(cè)或者底部)和狀態(tài)欄(StatusBar, 位于頂部, 可下拉)兩個(gè)部分。
StatusBar頂部狀態(tài)欄由三部分組成:
??1、最左邊的一部分顯示運(yùn)營(yíng)商水醋,時(shí)間管削,通知圖標(biāo)。
??2罗心、最右邊的一部分顯示系統(tǒng)圖標(biāo)里伯,它由狀態(tài)圖標(biāo)(例如 wifi ,bt)和電池圖標(biāo)組成。
??3渤闷、中間還有一塊區(qū)域疾瓮。
??從上圖可以比較直觀的看出來頂層樹是super_status_bar,之后會(huì)走兩個(gè)分支status_bar_container和status_bar_expanded飒箭,status_bar_container這個(gè)分支主要呈現(xiàn)的是狀態(tài)欄界面狼电,狀態(tài)欄細(xì)分左邊和右邊,左邊是通知欄弦蹂,右邊是系統(tǒng)功能的狀態(tài)圖標(biāo)顯示肩碟,status_bar_expanded這個(gè)分支主要呈現(xiàn)的下拉菜單界面,其實(shí)下拉菜單中又分快捷圖標(biāo)和短信通知欄凸椿。
二削祈、StatuBar的創(chuàng)建
??StatusBar也是繼承SystemUI,啟動(dòng)流程和SystemUI一致脑漫。并在start的時(shí)候添加創(chuàng)建StatusBar相關(guān)的view髓抑。
??我們從StatusBar的start()方法開始看。
frameworks\base\packages\SystemUI\src\com\android\systemui\statusbar\phone\StatusBar.java
@Override
public void start() {
// 省略部分代碼......
// 創(chuàng)建整個(gè)SystemUI視圖并添加到WindowManager中
createAndAddWindows();//這個(gè)重點(diǎn)方法窿撬,創(chuàng)建相關(guān)的視圖
// 省略部分代碼......
}
public void createAndAddWindows(@Nullable RegisterStatusBarResult result) {
// 創(chuàng)建整個(gè)SystemUI視圖
makeStatusBarView(result);
// 把視圖添加到Window中
mStatusBarWindowController = Dependency.get(StatusBarWindowController.class); //管理狀態(tài)欄启昧,導(dǎo)航欄、面板的狀態(tài)切換
mStatusBarWindowController.add(mStatusBarWindow, getStatusBarHeight());
}
根據(jù)上面代碼可知劈伴,視圖的創(chuàng)建在makeStatusbarView(result)方法中密末;
//創(chuàng)建狀態(tài)欄握爷,即初始化通知圖標(biāo)區(qū)域
protected void makeStatusBarView(RegisterStatusBarResult result) {
//1.初始化資源文件:導(dǎo)航欄高度 狀態(tài)欄高度 通知面板位置和高度等
final Context context = mContext;
updateDisplaySize(); // populates mDisplayMetrics
updateResources();
updateTheme();
//加載layout文件super_status_bar,mStatusBarWindow
inflateStatusBarWindow(context);
// ...
FragmentHostManager.get(mStatusBarWindow)
.addTagListener(CollapsedStatusBarFragment.TAG, (tag, fragment) -> {
CollapsedStatusBarFragment statusBarFragment =
(CollapsedStatusBarFragment) fragment;
// 把控制器中的通知容器加入到狀態(tài)欄的容器中
statusBarFragment.initNotificationIconArea(mNotificationIconAreaController);
// ...
}).getFragmentManager()
.beginTransaction()
// CollapsedStatusBarFragment實(shí)現(xiàn)了狀態(tài)欄的添加
.replace(R.id.status_bar_container, new CollapsedStatusBarFragment(),
CollapsedStatusBarFragment.TAG)
.commit();
// ...
}
??注意在這里添加了CollapsedStatusBarFragment严里,這個(gè)CollapsedStatusBarFragment就是作為加載狀態(tài)欄的Fragment新啼。
??在看 CollapsedStatusBarFragment 之前,我們先看看 NotificationIconAreaController(通知圖標(biāo)區(qū)域控制器)刹碾,在 NotificationIconAreaController 的構(gòu)造函數(shù)中會(huì)調(diào)用如下方法來創(chuàng)建通知圖標(biāo)的容器燥撞,即初始化布局。
// frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
protected void initializeNotificationAreaViews(Context context) {
// ...
LayoutInflater layoutInflater = LayoutInflater.from(context);
// 實(shí)例化通知圖標(biāo)區(qū)域視圖
mNotificationIconArea = inflater.inflate(R.layout.notification_icon_area, null);
// 這個(gè)才是真正存放通知圖標(biāo)的父容器
mNotificationIcons = mNotificationIconArea.findViewById(R.id.notificationIcons);
// ...
}
??這個(gè)類加載的是 R.layout.notification_icon_are.xml 布局迷帜。這里先回到 makeStatusBarView(RegisterStatusBarResult result) 方法物舒,我們看 statusBarFragment.initNotificationIconArea(mNotificationIconAreaController) ,這句話的作用就是 將 NotificationIconAreaController 自己創(chuàng)建的通知圖標(biāo)容器戏锹,加入到狀態(tài)欄視圖中冠胯。
??接下來繼續(xù)看 CollapsedStatusBarFragment,在看之前我們應(yīng)該帶著一個(gè)問題去看:這些圖標(biāo)都是動(dòng)態(tài)添加的锦针,是怎么被添加和移除的荠察。
??由 CollapsedStatusBarFragment onCreateView() 可知,加載的布局是 R.layout.status_bar奈搜。
??在該布局里包含了這么幾個(gè)關(guān)鍵的視圖:
??1悉盆、@+id/status_bar_contents 這個(gè)LinearLayout,里面包含了@+id/status_bar_left_side 馋吗,從名字來看就知道是狀態(tài)欄左邊部分焕盟。這個(gè)狀態(tài)欄左邊部分包含了時(shí)鐘@+id/clock和短信通知@+id/notification_icon_area。
??2宏粤、@+id/system_icon_area這個(gè)也是一個(gè)LinearLayout包含了
@layout/system_icons京髓,這部分就是狀態(tài)欄右邊部分,里面包含了電池圖標(biāo)和系統(tǒng)狀態(tài)圖標(biāo)等等商架。
由 statusBarFragment.initNotificationIconArea(mNotificationIconAreaController) 可知,會(huì)進(jìn)入到CollapsedStatusBarFragment 的 initNotificationIconArea 方法里
// frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
public void initNotificationIconArea(NotificationIconAreaController
notificationIconAreaController) {
// 通知圖標(biāo)區(qū)域
ViewGroup notificationIconArea = mStatusBar.findViewById(R.id.notification_icon_area);
// 這個(gè)才是通知圖標(biāo)的父容器
mNotificationIconAreaInner =
notificationIconAreaController.getNotificationInnerAreaView();
//移除自己芥玉,再添加自己
if (mNotificationIconAreaInner.getParent() != null) {
((ViewGroup) mNotificationIconAreaInner.getParent())
.removeView(mNotificationIconAreaInner);
}
// 把通知圖標(biāo)父容器添加到通知圖標(biāo)區(qū)域里
notificationIconArea.addView(mNotificationIconAreaInner);
// 省略處理中心圖標(biāo)區(qū)域的代碼
// 這里其實(shí)顯示了通知圖標(biāo)區(qū)域和中心圖標(biāo)區(qū)域
showNotificationIconArea(false);
}
三蛇摸、監(jiān)聽通知的服務(wù)端
??當(dāng)一條新通知發(fā)送后,它會(huì)存儲(chǔ)到通知服務(wù)端灿巧,也就是NotificationManagerService赶袄,那么SystemUI是如何知道新通知來臨的。這就需要SystemUI向NotificationManagerService注冊(cè)一個(gè)"服務(wù)"(一個(gè)Binder)抠藕。
??這個(gè)"服務(wù)"就相當(dāng)于客戶端 SystemUI 在服務(wù)端 NotificationManagerService 注冊(cè)的一個(gè)回調(diào)饿肺。當(dāng)有通知來臨的時(shí)候,就會(huì)通過這個(gè)"服務(wù)"通知SystemUI盾似。通過 notificationListener.registerAsSystemService()敬辣,進(jìn)行注冊(cè)。
??可以來看下這個(gè)注冊(cè)的實(shí)現(xiàn):
/**
* Directly register this service with the Notification Manager.
*
* <p>Only system services may use this call. It will fail for non-system callers.
* Apps should ask the user to add their listener in Settings.
*
* @param context Context required for accessing resources. Since this service isn't
* launched as a real Service when using this method, a context has to be passed in.
* @param componentName the component that will consume the notification information
* @param currentUser the user to use as the stream filter
* @hide
* @removed
*/
@SystemApi
public void registerAsSystemService(Context context, ComponentName componentName,
int currentUser) throws RemoteException {
if (mWrapper == null) {
// 這就是要注冊(cè)的Binder,也就一個(gè)回調(diào)
mWrapper = new NotificationListenerWrapper();
}
mSystemContext = context;
INotificationManager noMan = getNotificationInterface();
// ...
// 向通知的服務(wù)端注冊(cè)回調(diào)
noMan.registerListener(mWrapper, componentName, currentUser);
}
SystemUI注冊(cè)的這個(gè)“服務(wù)”其實(shí)就是:NotificationListenerWithPlugins溉跃。
注冊(cè)成功后村刨,就會(huì)回調(diào)NotificationListenerWithPlugins中的相關(guān)方法,并會(huì)附帶所有的通知信息撰茎。
四嵌牺、通知圖標(biāo)的顯示
當(dāng)一條新的通知來臨的時(shí)候,通知的服務(wù)端會(huì)通過NotificationListenerWithPlugins中的onNotificationPosted()進(jìn)行回調(diào)龄糊,而最終會(huì)調(diào)用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)) {
// 省略部分代碼...
// 進(jìn)行回調(diào)
handler.onNotificationPosted(sbn, rankingMap);
// 省略部分代碼 ...
}
}
這個(gè)回調(diào),將調(diào)到 NotificationEntryManager 類里的 onNotificationPosted()方法炫惩。
public void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) {
final boolean isUpdateToInflatedNotif = mActiveNotifications.containsKey(sbn.getKey());
if (isUpdateToInflatedNotif) {
updateNotification(sbn, rankingMap);
} else {
addNotification(sbn, rankingMap);
}
}
這里只關(guān)注 addNotification(sbn, rankingMap) 僻弹,而它內(nèi)部時(shí)調(diào)用 addNotificationInternal() 方法實(shí)現(xiàn)的。
private void addNotificationInternal(
StatusBarNotification notification,
RankingMap rankingMap) throws InflationException {
// 省略部分代碼 ...
NotificationEntry entry = mPendingNotifications.get(key);
// 省略部分代碼 ...
// Construct the expanded view.
if (!mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
mNotificationRowBinderLazy.get()
.inflateViews(
entry,
() -> performRemoveNotification(notification, REASON_CANCEL),
mInflationCallback);
}
// 省略部分代碼 ...
}
首先為通知?jiǎng)?chuàng)建一個(gè)NotificationEntry實(shí)例诡必,然后再通過NotificationRowBinderImpl中的inflateViews()加載通知視圖奢方,綁定通知信息,并在通知欄添加通知視圖爸舒,以及在狀態(tài)欄添加通知圖標(biāo)蟋字。
到此獲取到了數(shù)據(jù)、圖標(biāo)后面就是更新視圖進(jìn)行顯示了扭勉,由 NotificationRowBinderImpl#inflateViews → NotificationEntryManager#onAsyncInflationFinished → StatusBarNotificationPresenter#updateNotificationViews
// frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
public void updateNotificationViews() {
// ...
// 把通知視圖添加到通知面版的通知欄中
mViewHierarchyManager.updateNotificationViews();
// 這里不僅僅更新了通知面版的通知視圖鹊奖,也更新了狀態(tài)欄的通知圖標(biāo)
mNotificationPanel.updateNotificationViews(reason);
// ...
}
最終會(huì)調(diào)用 NotificationIconAreaController#updateNotificationIcons()進(jìn)行視圖更新。