在android 系统中有两个StatusBar ,
锁屏: keyguard_status_bar.xml
解锁:super_status_bar.xml
解锁 StatusBar
在SystemUI启动的的时候最终会调用 CoreStartable 实现类中 start() 方法, 具体过程可以参考:Android14 SystemUI 启动过程,
阅读SystemUI 代码可知道在 CentralSurfacesImpl
启动的时候会添加 StatusBar 到 Windows 上, 下面开始看具体代码
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
public void start() {
// 省略部分代码
mBarService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
mWallpaperSupported = mWallpaperManager.isWallpaperSupported();
RegisterStatusBarResult result = null;
try {
result = mBarService.registerStatusBar(mCommandQueue);
} catch (RemoteException ex) {
ex.rethrowFromSystemServer();
}
createAndAddWindows(result);
// 省略部分代码
}
public void createAndAddWindows(@Nullable RegisterStatusBarResult result) {
makeStatusBarView(result);
mNotificationShadeWindowController.attach();
mStatusBarWindowController.attach();
}
在这里我们看到调用了 mStatusBarWindowController.attach();
下面看一下mStatusBarWindowController
是何时创建的
@Inject
public CentralSurfacesImpl(
// 省略部分代码
StatusBarWindowController statusBarWindowController,
// 省略部分代码
) {
// 省略部分代码
mStatusBarWindowController = statusBarWindowController;
// 省略部分代码
}
在 CentralSurfacesImpl
的构造方法中注入的。 再看一下 StatusBarWindowController
创建的时候做了些什么
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java
@Inject
public StatusBarWindowController(
Context context,
@StatusBarWindowModule.InternalWindowView StatusBarWindowView statusBarWindowView,
WindowManager windowManager,
IWindowManager iWindowManager,
StatusBarContentInsetsProvider contentInsetsProvider,
FragmentService fragmentService,
@Main Resources resources,
Optional<UnfoldTransitionProgressProvider> unfoldTransitionProgressProvider) {
mContext = context;
mWindowManager = windowManager;
mIWindowManager = iWindowManager;
mContentInsetsProvider = contentInsetsProvider;
mStatusBarWindowView = statusBarWindowView;
mFragmentService = fragmentService;
mLaunchAnimationContainer = mStatusBarWindowView.findViewById(
R.id.status_bar_launch_animation_container);
mLpChanged = new WindowManager.LayoutParams();
if (mBarHeight < 0) {
mBarHeight = SystemBarUtils.getStatusBarHeight(mContext);
}
unfoldTransitionProgressProvider.ifPresent(
unfoldProgressProvider -> unfoldProgressProvider.addCallback(
new JankMonitorTransitionProgressListener(
/* attachedViewProvider=*/ () -> mStatusBarWindowView)));
}
上面代码可以发现在构造的时候注入了StatusBarWindowView
对象并且计算了StatusBar 的高度。
下面看一下StatusBarWindowView
注入过程,这里需要先看一下StatusBarWindowModule
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowModule.kt
@Module
abstract class StatusBarWindowModule {
/**
* Provides a [StatusBarWindowView].
*
* Only [StatusBarWindowController] should inject the view.
*/
@Module
companion object {
@JvmStatic
@Provides
@SysUISingleton
@InternalWindowView
fun providesStatusBarWindowView(layoutInflater: LayoutInflater): StatusBarWindowView {
return layoutInflater.inflate(
R.layout.super_status_bar,
/* root= */null
) as StatusBarWindowView?
?: throw IllegalStateException(
"R.layout.super_status_bar could not be properly inflated"
)
}
}
@Retention(AnnotationRetention.BINARY)
@Qualifier
protected annotation class InternalWindowView
}
这是一个dagger注入的module, 里面主要负责注入 StatusBarWindowView
的, 但是这里需要注意一下, 这里inflater 了 StatusBar的 layout( super_status_bar ), 可以知道 StatusBarWindowView
是 StatusBar 的 RootView。
再继续看一下StatusBar 的高度是如何获取的
frameworks/base/core/java/com/android/internal/policy/SystemBarUtils.java
/**
* Gets the status bar height.
*/
public static int getStatusBarHeight(Context context) {
return getStatusBarHeight(context.getResources(), context.getDisplay().getCutout());
}
/**
* Gets the status bar height with a specific display cutout.
*/
public static int getStatusBarHeight(Resources res, DisplayCutout cutout) {
final int defaultSize = res.getDimensionPixelSize(R.dimen.status_bar_height_default);
final int safeInsetTop = cutout == null ? 0 : cutout.getSafeInsetTop();
final int waterfallInsetTop = cutout == null ? 0 : cutout.getWaterfallInsets().top;
// The status bar height should be:
// Max(top cutout size, (status bar default height + waterfall top size))
return Math.max(safeInsetTop, defaultSize + waterfallInsetTop);
}
首先获取默认状态栏高度:defaultSize从资源文件中读取(R.dimen.status_bar_height_default), 获取安全插入区域顶部大小:safeInsetTop,如果没有cutout则为0, 获取瀑布流插入区域顶部大小:waterfallInsetTop,如果没有cutout则为0,这里需要注意, 刘海屏/挖孔屏是通过safeInsetTop控制, 曲面屏是通过waterfallInsetTop控制
最后在回到StatusBarWindowController
中看看一下attach
方法
public void attach() {
// Now that the status bar window encompasses the sliding panel and its
// translucent backdrop, the entire thing is made TRANSLUCENT and is
// hardware-accelerated.
Trace.beginSection("StatusBarWindowController.getBarLayoutParams");
mLp = getBarLayoutParams(mContext.getDisplay().getRotation());
Trace.endSection();
mWindowManager.addView(mStatusBarWindowView, mLp);
mLpChanged.copyFrom(mLp);
mContentInsetsProvider.addCallback(this::calculateStatusBarLocationsForAllRotations);
calculateStatusBarLocationsForAllRotations();
mIsAttached = true;
apply(mCurrentState);
}
这里主要将StatusBarWindowView
添加到 WindowManager
中显示
锁屏 StatusBar
锁屏StatusBar 加载过程要比解锁后加载要复杂一些。
先介绍一下keyguard_status_bar.xml
是如何被加载的
frameworks/base/packages/SystemUI/res/layout/status_bar_expanded.xml
<?xml version="1.0" encoding="utf-8"?>
<com.android.systemui.shade.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">
<com.android.systemui.shade.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">
<include
layout="@layout/keyguard_status_bar"
android:visibility="invisible" />
</com.android.systemui.shade.NotificationsQuickSettingsContainer>
</com.android.systemui.shade.NotificationPanelView>
可以看到status_bar_expanded.xml
中加载了keyguard_status_bar.xml
在看一下status_bar_expanded.xml
是如何被加载的
frameworks/base/packages/SystemUI/res/layout/super_notification_shade.xml
<?xml version="1.0" encoding="utf-8"?>
<!-- This is the notification shade window. -->
<com.android.systemui.shade.NotificationShadeWindowView
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.LightRevealScrim
android:id="@+id/light_reveal_scrim"
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" />
</com.android.systemui.shade.NotificationShadeWindowView>
可以看到super_notification_shade.xml
中加载了status_bar_expanded.xml
总结一下:
keyguard_status_bar.xml
-> status_bar_expanded.xml
-> super_notification_shade.xml
在super_notification_shade.xml
中 RootView 是 NotificationShadeWindowView
布局文件先介绍到这里,这里需要记住这个RootView
下面看代码部分,继续看CentralSurfacesImpl
这个类中的createAndAddWindows
方法
public void createAndAddWindows(@Nullable RegisterStatusBarResult result) {
makeStatusBarView(result);
mNotificationShadeWindowController.attach();
mStatusBarWindowController.attach();
}
这里调用到了 makeStatusBarView
protected void makeStatusBarView(@Nullable RegisterStatusBarResult result) {
inflateStatusBarWindow();
}
protected NotificationShadeWindowView mNotificationShadeWindowView;
protected final NotificationShadeWindowController mNotificationShadeWindowController;
private void inflateStatusBarWindow() {
mNotificationShadeWindowView = mCentralSurfacesComponent.getNotificationShadeWindowView();
mNotificationShadeWindowController.setNotificationShadeView(mNotificationShadeWindowView);
}
在这里我们看到了上面布局文件中的RootView(NotificationShadeWindowView
), 那么这个是怎么创建的的,
首先要看一下CentralSurfacesComponent
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java
public interface CentralSurfacesComponent {
/**
* Scope annotation for singleton items within the CentralSurfacesComponent.
*/
@Documented
@Retention(RUNTIME)
@Scope
@interface CentralSurfacesScope {}
/**
* Creates a {@link NotificationShadeWindowView}.
*/
NotificationShadeWindowView getNotificationShadeWindowView();
}
frameworks/base/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
@Module
abstract class ShadeModule {
@Binds
@IntoMap
@ClassKey(AuthRippleController::class)
abstract fun bindAuthRippleController(controller: AuthRippleController): CoreStartable
companion object {
const val SHADE_HEADER = "large_screen_shade_header"
@Provides
@SysUISingleton
// TODO(b/277762009): Do something similar to
// {@link StatusBarWindowModule.InternalWindowView} so that only
// {@link NotificationShadeWindowViewController} can inject this view.
fun providesNotificationShadeWindowView(
layoutInflater: LayoutInflater,
): NotificationShadeWindowView {
return layoutInflater.inflate(R.layout.super_notification_shade, /* root= */ null)
as NotificationShadeWindowView?
?: throw IllegalStateException(
"R.layout.super_notification_shade could not be properly inflated"
)
}
}
}
调用CentralSurfacesComponent
中的getNotificationShadeWindowView()
方法会执行 ShadeModule
中 providesNotificationShadeWindowView
方法注入 inflate 后的 RootView(NotificationShadeWindowView
)
到这里RootView 创建完毕, 然后通过NotificationShadeWindowController
中setNotificationShadeView
将RootView和NotificationShadeWindowController
对象进行绑定。
这里说一下NotificationShadeWindowController
, 它是个接口, 真正的实现类是NotificationShadeWindowControllerImpl
再继续看CentralSurfacesImpl
这个类中的createAndAddWindows
方法
public void createAndAddWindows(@Nullable RegisterStatusBarResult result) {
makeStatusBarView(result);
mNotificationShadeWindowController.attach();
mStatusBarWindowController.attach();
}
执行NotificationShadeWindowControllerImpl
对象的attach()
方法,
frameworks/base/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
@Override
public void attach() {
mLp = new LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT,
LayoutParams.TYPE_NOTIFICATION_SHADE,
LayoutParams.FLAG_NOT_FOCUSABLE
| LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
| LayoutParams.FLAG_SPLIT_TOUCH
| LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
| LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
PixelFormat.TRANSLUCENT);
mLp.token = new Binder();
mLp.gravity = Gravity.TOP;
mLp.setFitInsetsTypes(0 /* types */);
mLp.setTitle("NotificationShade");
mLp.packageName = mContext.getPackageName();
mLp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
mLp.privateFlags |= PRIVATE_FLAG_OPTIMIZE_MEASURE;
mLp.privateFlags |= PRIVATE_FLAG_BEHAVIOR_CONTROLLED;
mLp.insetsFlags.behavior = BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
mWindowManager.addView(mNotificationShadeView, mLp);
}
这里主要将NotificationShadeView
添加到 WindowManager
中显示。
到这里StatusBar 的加载过程就介绍结束了。在介绍锁屏StatusBar 加载过程中后面可能会有疑问, 后面很多类名字为什么都是NotificationXXX
, 是因为这个地方主要功能是Notification
, 锁屏 StatusBar 只是一小部分