本文是基於Android 10代碼分析。java
ActivityManagerService 建立完成時會啓動 SystemUIandroid
// frameworks/base/services/java/com/android/server/SystemServer.java
private void startOtherServices() {
mActivityManagerService.systemReady(() -> {
try {
// AMS啓動完成時,開啓SystemUI
// 其實就是start一個叫SystemUIService的service
startSystemUi(context, windowManagerF);
} catch (Throwable e) {
reportWtf("starting System UI", e);
}
}
}
複製代碼
啓動 SystemUI 的入口是 SystemUIService
,它是四大組件之一的 Service。數組
// frameworks/base/packages/SystemUI/src/com/android/systemui/SystemUIService.java
public class SystemUIService extends Service {
@Override
public void onCreate() {
super.onCreate();
// 啓動各類服務
((SystemUIApplication) getApplication()).startServicesIfNeeded();
}
}
複製代碼
調用了 SystemUIApplication#startServicesIfNeeded()
來啓動各類服務,而這些服務不是四大組件之一的 Service, 而是繼承自 SystemUI
接口的服務,咱們稱之爲 SystemUI服務。app
// frameworks/base/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
public void startServicesIfNeeded() {
// 這個數組中定義了不少SystemUI服務
String[] names = getResources().getStringArray(R.array.config_systemUIServiceComponents);
// 啓動服務
startServicesIfNeeded(names);
}
複製代碼
SystemUI 要啓動的全部服務都是在數組 config_systemUIServiceComponents
中定義的,能夠看下這個數組的定義ide
// frameworks/base/packages/SystemUI/res/values/config.xml
<string-array name="config_systemUIServiceComponents" translatable="false">
<item>com.android.systemui.Dependency$DependencyCreator</item>
<item>com.android.systemui.util.NotificationChannels</item>
<item>com.android.systemui.statusbar.CommandQueue$CommandQueueStart</item>
<item>com.android.systemui.keyguard.KeyguardViewMediator</item>
<item>com.android.systemui.recents.Recents</item>
<item>com.android.systemui.volume.VolumeUI</item>
<item>com.android.systemui.stackdivider.Divider</item>
<item>com.android.systemui.SystemBars</item>
<item>com.android.systemui.usb.StorageNotification</item>
<item>com.android.systemui.power.PowerUI</item>
<item>com.android.systemui.media.RingtonePlayer</item>
<item>com.android.systemui.keyboard.KeyboardUI</item>
<item>com.android.systemui.pip.PipUI</item>
<item>com.android.systemui.shortcut.ShortcutKeyDispatcher</item>
<item>@string/config_systemUIVendorServiceComponent</item>
<item>com.android.systemui.util.leak.GarbageMonitor$Service</item>
<item>com.android.systemui.LatencyTester</item>
<item>com.android.systemui.globalactions.GlobalActionsComponent</item>
<item>com.android.systemui.ScreenDecorations</item>
<item>com.android.systemui.biometrics.BiometricDialogImpl</item>
<item>com.android.systemui.SliceBroadcastRelayHandler</item>
<item>com.android.systemui.SizeCompatModeActivityController</item>
<item>com.android.systemui.statusbar.notification.InstantAppNotifier</item>
<item>com.android.systemui.theme.ThemeOverlayController</item>
</string-array>
複製代碼
對這些 SystemUI服務 作一點介紹佈局
com.android.systemui.Dependency
是爲了建立全局可用的依賴關係。com.android.systemui.SystemBars
建立整個SystemUI視圖的入口類。com.android.systemui.statusbar.CommandQueue
是一個 Binder 類,它會被StatusBar
註冊到 StatusBarManagerService
中,用於接收StatusBarManagerService
服務端的消息。如今接着上面,來看下如何啓動這些 SystemUI服務ui
private void startServicesIfNeeded(String[] services) {
if (mServicesStarted) {
return;
}
mServices = new SystemUI[services.length];
if (!mBootCompleted) {
// check to see if maybe it was already completed long before we began
// see ActivityManagerService.finishBooting()
// sys.boot_completed 屬性能夠判斷系統是否已經啓動完畢
if ("1".equals(SystemProperties.get("sys.boot_completed"))) {
mBootCompleted = true;
}
}
final int N = services.length;
for (int i = 0; i < N; i++) {
String clsName = services[i];
Class cls;
try {
// 1. 經過反射建立實例
cls = Class.forName(clsName);
Object o = cls.newInstance();
if (o instanceof SystemUI.Injector) {
o = ((SystemUI.Injector) o).apply(this);
}
// 強轉爲SystemUI保存
mServices[i] = (SystemUI) o;
} catch(Exception ex){
throw new RuntimeException(ex);
}
mServices[i].mContext = this;
// 注意這裏把mComponents也傳進去了,用於全局保存變量,Class<?> -> Object
mServices[i].mComponents = mComponents;
// 2. 調用start()啓動
mServices[i].start();
// 3. 系統啓動完畢,還用啓動相應的onBootCompleted()
if (mBootCompleted) {
mServices[i].onBootCompleted();
}
}
mServicesStarted = true;
}
複製代碼
首先根據 SystemUI服務的類名,經過反制加載類而且建立類的對象,而後調用對象的 start()
方法和 onBootCompleted()
方法。this
如今把目光集中到如何建立 StatusBar 上。前面說過,SystemBars
這個 SystemUI服務是整個System視圖建立的入口類,它被啓動時會調用 start()
方法spa
// frameworks/base/packages/SystemUI/src/com/android/systemui/SystemBars.java
public void start() {
// 也就是調用 StatusBar#start()
createStatusBarFromConfig();
}
複製代碼
createStatusBarFromConfig()
很簡單,就是調用了StatusBar
類的start()
方法。code
StatusBar#start()
代碼很龐大,本文主要分析狀態欄的建立,至於狀態欄上圖標的添加留到下一篇文章進行分析。
// packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
public void start() {
// 建立整個SystemUI視圖並添加到WindowManager中
createAndAddWindows(result);
}
public void createAndAddWindows(@Nullable RegisterStatusBarResult result) {
// 建立整個SystemUI視圖
makeStatusBarView(result);
// 把視圖添加到Window中
mStatusBarWindowController = Dependency.get(StatusBarWindowController.class);
mStatusBarWindowController.add(mStatusBarWindow, getStatusBarHeight());
}
複製代碼
makeStatusBarView()
負責建立整個SystemUI視圖,其中包括狀態欄。下面的代碼主要提取了建立視圖的代碼,分幾步進行分析
// packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
protected void makeStatusBarView(@Nullable RegisterStatusBarResult result) {
// ...
// 1. 實例化整個SystemUI視圖,包括狀態欄,通知面版, 鎖屏
mStatusBarWindow = (StatusBarWindowView) mInjectionInflater.injectable(
LayoutInflater.from(context)).inflate(R.layout.super_status_bar, null);
}
複製代碼
首先建立整個SystemUI視圖,它的佈局是 super_status_bar.xml
,能夠大體來看下佈局
<com.android.systemui.statusbar.phone.StatusBarWindowView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:sysui="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true">
<!--這裏省略了可有可無的視圖-->
<!--狀態欄容器-->
<FrameLayout android:id="@+id/status_bar_container" android:layout_width="match_parent" android:layout_height="wrap_content" />
<!--整個下拉通知面版,包括鎖屏界面-->
<include layout="@layout/status_bar_expanded" android:layout_width="match_parent" android:layout_height="match_parent" android:visibility="invisible" />
<!--這裏省略了可有可無的視圖-->
<!--這個顯示在鎖屏底部區域-->
<LinearLayout android:id="@+id/lock_icon_container" android:orientation="vertical" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="@dimen/status_bar_height" android:layout_gravity="top|center_horizontal">
<com.android.systemui.statusbar.phone.LockIcon android:id="@+id/lock_icon" android:layout_width="@dimen/keyguard_lock_width" android:layout_height="@dimen/keyguard_lock_height" android:layout_gravity="center_horizontal" android:layout_marginTop="@dimen/keyguard_lock_padding" android:contentDescription="@string/accessibility_unlock_button" android:src="@*android:drawable/ic_lock" android:scaleType="center" />
<com.android.keyguard.KeyguardMessageArea android:id="@+id/keyguard_message_area" style="@style/Keyguard.TextView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="@dimen/keyguard_lock_padding" android:gravity="center" android:singleLine="true" android:ellipsize="marquee" android:focusable="true" />
</LinearLayout>
</com.android.systemui.statusbar.phone.StatusBarWindowView>
複製代碼
根視圖StatusBarWindowView
是一個FrameLayout
,那麼狀態欄顯示在最下面,而後通知面版會覆蓋狀態欄,最後還有一個底部視圖在最上面。
能夠注意到狀態欄的窗口ID爲status_bar_container
,一會就會向這個容器中添加狀態欄視圖。如今接着上面的makeStatusBarView()
繼續分析
// packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
protected void makeStatusBarView(@Nullable RegisterStatusBarResult result) {
// ...
// 1. 實例化整個SystemUI視圖,包括狀態欄,通知面版, 鎖屏
mStatusBarWindow = (StatusBarWindowView) mInjectionInflater.injectable(
LayoutInflater.from(context)).inflate(R.layout.super_status_bar, null);
// 2.建立狀態欄視圖
FragmentHostManager.get(mStatusBarWindow)
.addTagListener(CollapsedStatusBarFragment.TAG, (tag, fragment) -> {
CollapsedStatusBarFragment statusBarFragment =
(CollapsedStatusBarFragment) fragment;
// 用通知圖標控制器,初始化了通知圖標區域和中心圖標區域,而且顯示出來
statusBarFragment.initNotificationIconArea(mNotificationIconAreaController);
}).getFragmentManager()
.beginTransaction()
// CollapsedStatusBarFragment實現了狀態欄的添加
.replace(R.id.status_bar_container, new CollapsedStatusBarFragment(),
CollapsedStatusBarFragment.TAG)
.commit();
}
複製代碼
能夠看到CollapsedStatusBarFragment
表明的就是狀態欄視圖,這個視圖被添加到ID爲status_bar_container
的容器中。接下來只要分析CollapsedStatusBarFragment
的生命週期,便可知道狀態欄的建立過程,首先看的就是CollapsedStatusBarFragment#onCreateView()
方法,這裏就是建立視圖的地方
// frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
// status bar視圖
return inflater.inflate(R.layout.status_bar, container, false);
}
複製代碼
status_bar.xml
就是狀態欄視圖佈局,接下來再簡單看下佈局狀況
<com.android.systemui.statusbar.phone.PhoneStatusBarView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui" android:layout_width="match_parent" android:layout_height="@dimen/status_bar_height" android:id="@+id/status_bar" android:background="@drawable/system_bar_background" android:orientation="vertical" android:focusable="false" android:descendantFocusability="afterDescendants" android:accessibilityPaneTitle="@string/status_bar" >
<!--這裏省略了可有可無的視圖-->
<LinearLayout android:id="@+id/status_bar_contents" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingStart="@dimen/status_bar_padding_start" android:paddingEnd="@dimen/status_bar_padding_end" android:paddingTop="@dimen/status_bar_padding_top" android:orientation="horizontal" >
<FrameLayout android:layout_height="match_parent" android:layout_width="0dp" android:layout_weight="1">
<!--這個好像是在狀態欄顯示信息佈局-->
<include layout="@layout/heads_up_status_bar_layout" />
<!--狀態欄左邊視圖,依次顯示運營商名字,時間,通知圖標-->
<LinearLayout android:id="@+id/status_bar_left_side" android:layout_height="match_parent" android:layout_width="match_parent" android:clipChildren="false" >
<ViewStub android:id="@+id/operator_name" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout="@layout/operator_name" />
<com.android.systemui.statusbar.policy.Clock android:id="@+id/clock" android:layout_width="wrap_content" android:layout_height="match_parent" android:textAppearance="@style/TextAppearance.StatusBar.Clock" android:singleLine="true" android:paddingStart="@dimen/status_bar_left_clock_starting_padding" android:paddingEnd="@dimen/status_bar_left_clock_end_padding" android:gravity="center_vertical|start" />
<!--通知圖標區域-->
<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"/>
</LinearLayout>
</FrameLayout>
<!--這裏省略了可有可無的視圖-->
<!--中心圖標區域-->
<com.android.systemui.statusbar.AlphaOptimizedFrameLayout android:id="@+id/centered_icon_area" android:layout_width="wrap_content" android:layout_height="match_parent" android:orientation="horizontal" android:clipChildren="false" android:gravity="center_horizontal|center_vertical"/>
<!--系統圖標區,由狀態圖標集和電池圖標組成,狀態圖標集由wifi,bt等等組成-->
<com.android.keyguard.AlphaOptimizedLinearLayout android:id="@+id/system_icon_area" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:orientation="horizontal" android:gravity="center_vertical|end" >
<include layout="@layout/system_icons" />
</com.android.keyguard.AlphaOptimizedLinearLayout>
</LinearLayout>
<!--這裏省略了可有可無的視圖-->
</com.android.systemui.statusbar.phone.PhoneStatusBarView>
複製代碼
從這個佈局能夠分析出狀態欄從左到右到底顯示了什麼。
這樣一來,咱們就對整個狀態欄上的佈局有個瞭解,接下來分析下狀態上的狀態圖標(例如bt, wifi)是如何顯示上去的,這就是後面一篇文章分析的主要內容。