本文是基于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)是如何显示上去的,这就是后面一篇文章分析的主要内容。