前言
上一篇我们具体分析了SystemUI的启动流程,在SystemServer的startOtherServices方法中,会启动SystemUIService服务,SystemUIService服务的onCreate方法会继续调用SystemUIApplication的startServicesIfNeeded方法,在该方法中会获取SystemUI组件各个类的具体路径,并通过反射创建对应的实例对象,然后依次调用每个组件的start() 方法启动相关类的服务,启动完成后,又会再次调用该组件的onBootCompleted( ) 方法。
framework/base/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
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].mContext = this;
mServices[i].mComponents = mComponents;
mServices[i].start(); //调用start()启动
...
if (mBootCompleted) {
mServices[i].onBootCompleted(); //启动完毕,调用对应的onBootCompleted()方法
}
}
...
mServicesStarted = true;
}
frameworks/base/packages/SystemUI/res/values/config.xml
<string-array name="config_systemUIServiceComponents" translatable="false">
<item>com.android.systemui.Dependency</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.fingerprint.FingerprintDialogImpl</item>
<item>com.android.systemui.SliceBroadcastRelayHandler</item>
</string-array>
对这些 SystemUI服务 做一点介绍:
- com.android.systemui.Dependency 是为了创建全局可用的依赖关系。
- com.android.systemui.SystemBars创建整个SystemUI视图的入口类。
- com.android.systemui.recents.Recents 最近任务
- com.android.systemui.volume.VolumeUI 音量控制
- com.android.systemui.pip.PipUI 画中画
- com.android.systemui.statusbar.CommandQueue 是一个 Binder 类,它会被StatusBar注册到 StatusBarManagerService 中,用于接收StatusBarManagerService服务端的消息。
一、创建StatusBar
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.StatusBar</string>
该数值其实就是状态栏所对应类的完整路径,然后再通过反射创建出具体的StatusBar对象实例,最后调用StatusBar的start()方法。
2、StatusBar的start方法如下所示。
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 {
protected StatusBarWindowView mStatusBarWindow;//状态栏视图容器
protected StatusBarWindowManager mStatusBarWindowManager;//状态栏窗口管理者
@Override
public void start() {
...
createAndAddWindows(); //创建整个StatusBar视图并将其添加到Window中
...
}
...
public void createAndAddWindows() {
addStatusBarWindow();
}
private void addStatusBarWindow() {
makeStatusBarView(); //创建整个StatusBar视图
...
//调用StatusBarWindowManager的add方法,将视图添加到窗口
mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight());上
}
...
}
方法代码较多,这里我们只列出了几个关键点,首先是调用createAndAddWindows方法,该方法再调用addStatusBarWindow方法,addStatusBarWindow方法又会继续调用makeStatusBarView方法来构建系统状态栏视图,并将视图内容赋值给mStatusBarWindow,然后调用StatusBarWindowManager的add方法将状态栏视图添加到窗口上。
二、通过布局文件构建StatusBar所对应的视图
1、StatusBar的makeStatusBarView方法如下,该方法会继续调用inflateStatusBarWindow方法,将R.layout.super_status_bar布局文件构建成对应的视图。
public class StatusBar extends SystemUI implements DemoMode,
DragDownHelper.DragDownCallback, ActivityStarter, OnUnlockMethodChangedListener,
OnHeadsUpChangedListener, CommandQueue.Callbacks, ZenModeController.Callback,
ColorExtractor.OnColorsChangedListener, ConfigurationListener, NotificationPresenter {
protected void makeStatusBarView() {
final Context context = mContext;
updateDisplaySize(); // populates mDisplayMetrics
updateResources();
updateTheme();
//实例化整个statusBar视图,包括状态栏,通知面版,锁屏
inflateStatusBarWindow(context);
...
}
protected void inflateStatusBarWindow(Context context) {
//实例化整个statusBar视图,包括状态栏,通知面版,锁屏
mStatusBarWindow = (StatusBarWindowView) View.inflate(context, R.layout.super_status_bar, null);
}
}
2、布局super_status_bar.xmlz其实就是整个statusBar视图,它的的具体内容如下所示:
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" />
<!--状态栏图标下的SeekBar布局 -->
<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。
public class StatusBar extends SystemUI implements DemoMode,
DragDownHelper.DragDownCallback, ActivityStarter, OnUnlockMethodChangedListener,
OnHeadsUpChangedListener, CommandQueue.Callbacks, ZenModeController.Callback,
ColorExtractor.OnColorsChangedListener, ConfigurationListener, NotificationPresenter {
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 就是状态栏视图布局,该布局文件的具体内容如下所示:
frameworks/base/packages/SystemUI/res/layout/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"/>
<!--系统图标区,由状态图标集和电池图标组成,状态图标集由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>
<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),以及电池图标。
四、将StatusBar视图内容到窗口上
1、继续往下看StatusBar的addStatusBarWindow方法。
public class StatusBar extends SystemUI implements DemoMode,
DragDownHelper.DragDownCallback, ActivityStarter, OnUnlockMethodChangedListener,
OnHeadsUpChangedListener, CommandQueue.Callbacks, ZenModeController.Callback,
ColorExtractor.OnColorsChangedListener, ConfigurationListener, NotificationPresenter {
protected StatusBarWindowView mStatusBarWindow;//状态栏视图容器
protected StatusBarWindowManager mStatusBarWindowManager;//状态栏窗口管理者
private void addStatusBarWindow() {
makeStatusBarView(); //创建整个StatusBar视图
...
//调用StatusBarWindowManager的add方法,将StatusBar视图添加到窗口上
mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight());
}
protected void makeStatusBarView() {
...
//实例化整个statusBar视图,包括状态栏,通知面版,锁屏
inflateStatusBarWindow(context);
...
}
protected void inflateStatusBarWindow(Context context) {
//实例化整个statusBar视图,包括状态栏,通知面版,锁屏
mStatusBarWindow = (StatusBarWindowView) View.inflate(context, R.layout.super_status_bar, null);
}
}
在StatusBa调用makeStatusBarView创建状态栏视图容器之后,会调用StatusBarWindowManager的add方法将该视图容器添加到窗口上。
2、StatusBarWindowManager和add相关的代码如下所示。
public class StatusBarWindowManager implements RemoteInputController.Callback, Dumpable {
private final WindowManager mWindowManager;
public StatusBarWindowManager(Context context) {
mContext = context;
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
...代码省略...
}
public void add(View statusBarView, int barHeight) {
mLp = new WindowManager.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
barHeight,
WindowManager.LayoutParams.TYPE_STATUS_BAR,//窗口类型为状态栏
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
| WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
PixelFormat.TRANSLUCENT);
mLp.token = new Binder();
mLp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
mLp.gravity = Gravity.TOP;
mLp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
mLp.setTitle("StatusBar");//对用的Window的名称为StatusBar
mLp.packageName = mContext.getPackageName();
mStatusBarView = statusBarView;
mBarHeight = barHeight;
mWindowManager.addView(mStatusBarView, mLp);//将状态栏视图添加到WindowManager中
mLpChanged = new WindowManager.LayoutParams();
mLpChanged.copyFrom(mLp);
}
}
StatusBarWindowManager的构造方法首先获取WindowManager实例,然后创建调用WindowManager的add方法所需要的参数WindowManager.LayoutParams,结合源码可知状态栏所对应的窗口类型为WindowManager.LayoutParams.TYPE_STATUS_BAR,窗口名称为StatusBar。
五、创建导航栏视图内容并添加到窗口上
分析完了系统状态栏视图的创建和添加流程,我们再来分析一下系统导航栏视图的创建和添加流程。
1、系统导航栏的创建也是在StatusBar的makeStatusBarView方法中。
public class StatusBar extends SystemUI implements DemoMode,
DragDownHelper.DragDownCallback, ActivityStarter, OnUnlockMethodChangedListener,
OnHeadsUpChangedListener, CommandQueue.Callbacks, ZenModeController.Callback,
ColorExtractor.OnColorsChangedListener, ConfigurationListener, NotificationPresenter {
protected IWindowManager mWindowManagerService;
protected void makeStatusBarView() {
...
try {
//判断是否存在导航栏
boolean showNav = mWindowManagerService.hasNavigationBar();
if (DEBUG) Log.v(TAG, "hasNavigationBar=" + showNav);
if (showNav) {
createNavigationBar();
}
} catch (RemoteException ex) {
// no window manager? good luck with that
}
...
}
//创建导航栏视图
protected void createNavigationBar() {
mNavigationBarView = NavigationBarFragment.create(mContext, (tag, fragment) -> {
mNavigationBar = (NavigationBarFragment) fragment;
if (mLightBarController != null) {
mNavigationBar.setLightBarController(mLightBarController);
}
mNavigationBar.setCurrentSysuiVisibility(mSystemUiVisibility);
});
}
}
在创建完StatusBar所对应的视图之后,会调用IWindowManager的hasNavigationBar方法来判断系统是否存在导航栏,如果存在则会调用createNavigationBar方法,该方法会进一步调用NavigationBarFragment的静态方法create来创建导航栏视图。
2、NavigationBarFragment的create方法如下所示。
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
public class NavigationBarFragment extends Fragment implements Callbacks {
public static View create(Context context, FragmentListener listener) {
WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,//窗口类型为导航栏
WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
| WindowManager.LayoutParams.FLAG_SLIPPERY,
PixelFormat.TRANSLUCENT);
lp.token = new Binder();
lp.setTitle("NavigationBar");//对用的Window的名称为NavigationBar
lp.accessibilityTitle = context.getString(R.string.nav_bar);
lp.windowAnimations = 0;
View navigationBarView = LayoutInflater.from(context).inflate(
R.layout.navigation_bar_window, null);
if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + navigationBarView);
if (navigationBarView == null) return null;
context.getSystemService(WindowManager.class).addView(navigationBarView, lp);//将导航栏视图添加到WindowManager中
FragmentHostManager fragmentHost = FragmentHostManager.get(navigationBarView);
NavigationBarFragment fragment = new NavigationBarFragment();
fragmentHost.getFragmentManager().beginTransaction()
.replace(R.id.navigation_bar_frame, fragment, TAG)
.commit();
fragmentHost.addTagListener(TAG, listener);
return navigationBarView;
}
}
create方法首先创建调用WindowManager的add方法所需要的参数WindowManager.LayoutParams,结合源码可知导航栏所对应的窗口类型为WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,窗口名称为NavigationBar。