上一篇我们具体分析了SystemUI的启动流程,在SystemServer的startOtherServices方法中,会启动SystemUIService服务,SystemUIService服务的onCreate方法会继续调用SystemUIApplication的startServicesIfNeeded方法,在该方法中会获取SystemUI组件各个类的具体路径,并通过反射创建对应的实例对象,然后依次调用每个组件的start() 方法启动相关类的服务,启动完成后,又会再次调用该组件的onBootCompleted( ) 方法。
private void startServicesIfNeeded(String[] services) {
...
mServices = new SystemUI[services.length];
...
final int N = services.length;
for (int i = 0; i < N; i++) {
String clsName = services[i];//具体系统控件类的完整路径
...
Class cls = Class.forName(clsName);
mServices[i] = (SystemUI) cls.newInstance();//通过反射创建实例对象
...
mServices[i].start(); //调用start()启动
...
if (mBootCompleted) {
mServices[i].onBootCompleted(); //启动完毕,调用对应的onBootCompleted()方法
}
}
...
mServicesStarted = true;
}
<string-array name="config_systemUIServiceComponents" translatable="false">
<item>com.android.systemui.Dependencyitem>
<item>com.android.systemui.util.NotificationChannelsitem>
<item>com.android.systemui.statusbar.CommandQueue$CommandQueueStartitem>
<item>com.android.systemui.keyguard.KeyguardViewMediatoritem>
<item>com.android.systemui.recents.Recentsitem>
<item>com.android.systemui.volume.VolumeUIitem>
<item>com.android.systemui.stackdivider.Divideritem>
<item>com.android.systemui.SystemBarsitem>
<item>com.android.systemui.usb.StorageNotificationitem>
<item>com.android.systemui.power.PowerUIitem>
<item>com.android.systemui.media.RingtonePlayeritem>
<item>com.android.systemui.keyboard.KeyboardUIitem>
<item>com.android.systemui.pip.PipUIitem>
<item>com.android.systemui.shortcut.ShortcutKeyDispatcheritem>
<item>@string/config_systemUIVendorServiceComponentitem>
<item>com.android.systemui.util.leak.GarbageMonitor$Serviceitem>
<item>com.android.systemui.LatencyTesteritem>
<item>com.android.systemui.globalactions.GlobalActionsComponentitem>
<item>com.android.systemui.ScreenDecorationsitem>
<item>com.android.systemui.fingerprint.FingerprintDialogImplitem>
<item>com.android.systemui.SliceBroadcastRelayHandleritem>
string-array>
对这些 SystemUI服务 做一点介绍:
1、上面说过,SystemBars这个组件是整个SystemUI视图创建的入口类,它被启动时会调用 start() 方法,先看下该类的 start() 方法:
frameworks/base/packages/SystemUI/src/com/android/systemui/SystemBars.java
public class SystemBars extends SystemUI {
...
@Override
public void start() {
createStatusBarFromConfig();//通过配置创建系统状态栏
}
...
private void createStatusBarFromConfig() {
final String clsName = mContext.getString(R.string.config_statusBarComponent);//获取系统状态栏对应类的全路径
if (clsName == null || clsName.length() == 0) {
throw andLog("No status bar component configured", null);
}
Class<?> cls = null;
try {
cls = mContext.getClassLoader().loadClass(clsName);//加载对应的类对象
} catch (Throwable t) {
throw andLog("Error loading status bar component: " + clsName, t);
}
try {
mStatusBar = (SystemUI) cls.newInstance();//通过反射创建实例对象
} catch (Throwable t) {
throw andLog("Error creating status bar component: " + clsName, t);
}
mStatusBar.mContext = mContext;
mStatusBar.mComponents = mComponents;
mStatusBar.start();//调用系统状态栏的start方法
}
从中可以知道,该方法中先读取 value/config.xml文件中config_statusBarComponent的值
<string name="config_statusBarComponent">com.android.systemui.statusbar.phone.StatusBarstring>
该数值其实就是状态栏所对应类的完整路径,然后再通过反射创建出具体的StatusBar对象实例,最后调用StatusBar的start()方法。
2、StatusBar的start方法如下所示,该类的start()方法代码较多,这里我们只看重点,在该方法里面有调用createAndAddWindows()方法,然后再调用addStatusBarWindow()方法,该方法又会继续调用makeStatusBarView方法,makeStatusBarView方法具体来构建系统状态栏视图。
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
public class StatusBar extends SystemUI implements DemoMode,
DragDownHelper.DragDownCallback, ActivityStarter, OnUnlockMethodChangedListener,
OnHeadsUpChangedListener, CommandQueue.Callbacks, ZenModeController.Callback,
ColorExtractor.OnColorsChangedListener, ConfigurationListener, NotificationPresenter {
...
@Override
public void start() {
...
createAndAddWindows(); //创建整个SystemUI视图并添加到WindowManager中
...
}
...
public void createAndAddWindows() {
addStatusBarWindow();
}
private void addStatusBarWindow() {
makeStatusBarView(); //创建整个SystemUI视图
...
}
...
}
1、StatusBar的makeStatusBarView方法如下,该方法会继续调用inflateStatusBarWindow方法,将R.layout.super_status_bar布局文件构建成对应的视图。
protected void makeStatusBarView() {
final Context context = mContext;
updateDisplaySize(); // populates mDisplayMetrics
updateResources();
updateTheme();
//实例化整个SystemUI视图,包括状态栏,通知面版,锁屏
inflateStatusBarWindow(context);
...
}
protected void inflateStatusBarWindow(Context context) {
//实例化整个SystemUI视图,包括状态栏,通知面版,锁屏
mStatusBarWindow = (StatusBarWindowView) View.inflate(context, R.layout.super_status_bar, null);
}
2、布局super_status_bar.xmlz其实就是整个SystemUI视图,它的的具体内容如下所示:
frameworks/base/packages/SystemUI/res/layout/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">
<com.android.systemui.statusbar.BackDropView
android:id="@+id/backdrop"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"
sysui:ignoreRightInset="true"
>
<ImageView android:id="@+id/backdrop_back"
android:layout_width="match_parent"
android:scaleType="centerCrop"
android:layout_height="match_parent" />
<ImageView android:id="@+id/backdrop_front"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:visibility="invisible" />
com.android.systemui.statusbar.BackDropView>
<com.android.systemui.statusbar.ScrimView
android:id="@+id/scrim_behind"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:importantForAccessibility="no"
sysui:ignoreRightInset="true"
/>
<FrameLayout
android:id="@+id/status_bar_container"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<ViewStub android:id="@+id/fullscreen_user_switcher_stub"
android:layout="@layout/car_fullscreen_user_switcher"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<include layout="@layout/status_bar_expanded"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="invisible" />
<include layout="@layout/brightness_mirror" />
<com.android.systemui.statusbar.ScrimView
android:id="@+id/scrim_in_front"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:importantForAccessibility="no"
sysui:ignoreRightInset="true"
/>
com.android.systemui.statusbar.phone.StatusBarWindowView>
根视图StatusBarWindowView是一个FrameLayout,那么状态栏显示在最下面,然后通知面版会覆盖状态栏,最后还有一个底部视图在最上面。另外值得注意的一点就是id为status_bar_container的组件就是系统状态栏,接下来会向这个容器中添加状态栏视图。
1、继续看StatusBar的makeStatusBarView方法,我们就会发现,id为status_bar_container所对应的Fragment其实就是状态栏,而它所对应的具体实现类就是CollapsedStatusBarFragment。
protected void makeStatusBarView() {
final Context context = mContext;
updateDisplaySize();
updateResources();
updateTheme();
//实例化整个SystemUI视图,包括状态栏,通知面版,锁屏
inflateStatusBarWindow(context);
...
// 创建状态栏视图
FragmentHostManager.get(mStatusBarWindow)
.addTagListener(CollapsedStatusBarFragment.TAG, (tag, fragment) -> {
CollapsedStatusBarFragment statusBarFragment =
(CollapsedStatusBarFragment) fragment;
// 用通知图标控制器,初始化了通知图标区域和中心图标区域,并且显示出来
statusBarFragment.initNotificationIconArea(mNotificationIconAreaController);
mStatusBarView = (PhoneStatusBarView) fragment.getView();
mStatusBarView.setBar(this);
mStatusBarView.setPanel(mNotificationPanel);
mStatusBarView.setScrimController(mScrimController);
mStatusBarView.setBouncerShowing(mBouncerShowing);
if (mHeadsUpAppearanceController != null) {
// This view is being recreated, let's destroy the old one
mHeadsUpAppearanceController.destroy();
}
mHeadsUpAppearanceController = new HeadsUpAppearanceController(
mNotificationIconAreaController, mHeadsUpManager, mStatusBarWindow);
setAreThereNotifications();
checkBarModes();
}).getFragmentManager()
.beginTransaction()
// CollapsedStatusBarFragment实现了状态栏的添加
.replace(R.id.status_bar_container, new CollapsedStatusBarFragment(),
CollapsedStatusBarFragment.TAG)
.commit();
...
}
可以看到CollapsedStatusBarFragment代表的就是状态栏视图,这个视图被添加到ID为status_bar_container的容器中。
2、接下来继续分析CollapsedStatusBarFragment的生命周期,即可知道状态栏的创建过程。这里创建视图的地方就在CollapsedStatusBarFragment的onCreateView方法中,该方法会将status_bar.xml布局文件转化为对应的视图。
public class CollapsedStatusBarFragment extends Fragment implements CommandQueue.Callbacks {
...
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
Bundle savedInstanceState) {
//系统状态栏所对应的布局文件
return inflater.inflate(R.layout.status_bar, container, false);
}
...
}
3、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">
<ImageView
android:id="@+id/notification_lights_out"
android:layout_width="@dimen/status_bar_icon_size"
android:layout_height="match_parent"
android:paddingStart="@dimen/status_bar_padding_start"
android:paddingBottom="2dip"
android:src="@drawable/ic_sysbar_lights_out_dot_small"
android:scaleType="center"
android:visibility="gone"/>
<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:orientation="horizontal">
<ViewStub
android:id="@+id/operator_name"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout="@layout/operator_name" />
<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">
<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>
<android.widget.Space
android:id="@+id/cutout_space_view"
android:layout_width="0dp"
android:layout_height="match_parent"
android:gravity="center_horizontal|center_vertical"/>
<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>
<ViewStub
android:id="@+id/emergency_cryptkeeper_text"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout="@layout/emergency_cryptkeeper_text"/>
com.android.systemui.statusbar.phone.PhoneStatusBarView>
从这个布局可以分析出状态栏从左到右到底显示了什么。
这样一来,我们就对整个状态栏上的布局有个了解,后续的篇章我们将会继续分析下状态上的状态图标(例如bt, wifi)是如何显示上去的。
1、重新回到第二步,继续看StatusBar所对应的super_status_bar.xml,该布局文件中
include所包含的status_bar_expanded就是系统下拉的通知窗口的布局文件。
<com.android.systemui.statusbar.phone.NotificationPanelView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:systemui="http://schemas.android.com/apk/res-auto"
android:id="@+id/notification_panel"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/transparent" >
<include
layout="@layout/keyguard_status_view"
android:visibility="gone" />
<com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="@integer/notification_panel_layout_gravity"
android:id="@+id/notification_container_parent"
android:clipToPadding="false"
android:clipChildren="false">
<FrameLayout
android:id="@+id/qs_frame"
android:layout="@layout/qs_panel"
android:layout_width="@dimen/qs_panel_width"
android:layout_height="match_parent"
android:layout_gravity="@integer/notification_panel_layout_gravity"
android:clipToPadding="false"
android:clipChildren="false"
systemui:viewType="com.android.systemui.plugins.qs.QS" />
<com.android.systemui.statusbar.stack.NotificationStackScrollLayout
android:id="@+id/notification_stack_scroller"
android:layout_marginTop="@dimen/notification_panel_margin_top"
android:layout_width="@dimen/notification_panel_width"
android:layout_height="match_parent"
android:layout_gravity="@integer/notification_panel_layout_gravity"
android:layout_marginBottom="@dimen/close_handle_underlap" />
<include layout="@layout/ambient_indication"
android:id="@+id/ambient_indication_container" />
<ViewStub
android:id="@+id/keyguard_user_switcher"
android:layout="@layout/keyguard_user_switcher"
android:layout_height="match_parent"
android:layout_width="match_parent" />
<include
layout="@layout/keyguard_status_bar"
android:visibility="invisible" />
<Button
android:id="@+id/report_rejected_touch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/status_bar_header_height_keyguard"
android:text="@string/report_rejected_touch"
android:visibility="gone" />
com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer>
<include
layout="@layout/keyguard_bottom_area"
android:visibility="gone" />
<com.android.systemui.statusbar.AlphaOptimizedView
android:id="@+id/qs_navbar_scrim"
android:layout_height="96dp"
android:layout_width="match_parent"
android:layout_gravity="bottom"
android:visibility="invisible"
android:background="@drawable/qs_navbar_scrim" />
com.android.systemui.statusbar.phone.NotificationPanelView>