StatusBar继承SystemUI,在开机启动的时候,被SystemUIService调用StatusBar.start()执行初始化
//frameworks/base/packages/SystemUI/src/com/android/systemui/SystemUIService.java
@Override
public void onCreate() {
super.onCreate();
// Start all of SystemUI
((SystemUIApplication) getApplication()).startServicesIfNeeded();
...
}
//frameworks/base/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java)
public void startServicesIfNeeded() {
String[] names = SystemUIFactory.getInstance().getSystemUIServiceComponents(getResources());
startServicesIfNeeded(/* metricsPrefix= */ "StartServices", names);
}
//frameworks/base/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java)
public String[] getSystemUIServiceComponents(Resources resources) {
return resources.getStringArray(R.array.config_systemUIServiceComponents);
}
//frameworks/base/packages/SystemUI/res/values/config.xml)
<string-array name="config_systemUIServiceComponents" translatable="false">
315 <item>com.android.systemui.util.NotificationChannels</item>
316 <item>com.android.systemui.keyguard.KeyguardViewMediator</item>
317 <item>com.android.systemui.recents.Recents</item>
318 <item>com.android.systemui.volume.VolumeUI</item>
319 <item>com.android.systemui.statusbar.phone.StatusBar</item>
320 <item>com.android.systemui.usb.StorageNotification</item>
321 <item>com.android.systemui.power.PowerUI</item>
322 <item>com.android.systemui.media.RingtonePlayer</item>
323 <item>com.android.systemui.keyboard.KeyboardUI</item>
324 <item>com.android.systemui.shortcut.ShortcutKeyDispatcher</item>
325 <item>@string/config_systemUIVendorServiceComponent</item>
326 <item>com.android.systemui.util.leak.GarbageMonitor$Service</item>
327 <item>com.android.systemui.LatencyTester</item>
328 <item>com.android.systemui.globalactions.GlobalActionsComponent</item>
329 <item>com.android.systemui.ScreenDecorations</item>
330 <item>com.android.systemui.biometrics.AuthController</item>
331 <item>com.android.systemui.SliceBroadcastRelayHandler</item>
332<item>com.android.systemui.statusbar.notification.InstantAppNotifier</item>
333<item>com.android.systemui.theme.ThemeOverlayController</item>
334 <item>com.android.systemui.accessibility.WindowMagnification</item>
335 <item>com.android.systemui.accessibility.SystemActions</item>
336 <item>com.android.systemui.toast.ToastUI</item>
337 <item>com.android.systemui.wmshell.WMShell</item>
338 </string-array>
启动config.xml配置的服务(实际就是启动继承了SystemUI的java类)
private void startServicesIfNeeded(String metricsPrefix, String[] services) {
if (mServicesStarted) {
return;
}
mServices = new SystemUI[services.length];
...
try {
SystemUI obj = mComponentHelper.resolveSystemUI(clsName);
if (obj == null) {
Constructor constructor = Class.forName(clsName).getConstructor(Context.class);
obj = (SystemUI) constructor.newInstance(this);
}
mServices[i] = obj;
} catch (ClassNotFoundException
| NoSuchMethodException
| IllegalAccessException
| InstantiationException
| InvocationTargetException ex) {
throw new RuntimeException(ex);
}
...
mServices[i].start();
...
}
if (mBootCompleteCache.isBootComplete()) {
mServices[i].onBootCompleted();
}
mServicesStarted = true;
}
启动StatusBar(启动状态栏,通知栏,导航栏)
//frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java)
@Override
public void start() {
...
//获取IStatusBarService实例,
//IStatusBarService=StatusBarManagerService是系统服务,
//会保存状态栏的状态信息,运行在SystemServer进程。
//操作状态栏的请求会通过IStatusBarService发送给SystemUI。
//对SystemUI而言StatusBarManagerService更像客户端。
mBarService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
mKeyguardManager = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
...
// Connect in to the status bar manager service
mCommandQueue.addCallback(this);
RegisterStatusBarResult result = null;
try {
//CommandQueue继承IStatusBar.Stub,是IStatusBar的Bn端,
//通过注册保存到IStatusBarService中。
result = mBarService.registerStatusBar(mCommandQueue);
} catch (RemoteException ex) {
ex.rethrowFromSystemServer();
}
//创建状态栏,通知栏,导航栏
createAndAddWindows(result);
setUpPresenter();
...
// Set up the initial icon state
int numIcons = result.mIcons.size();
for (int i = 0; i < numIcons; i++) {
mCommandQueue.setIcon(result.mIcons.keyAt(i), result.mIcons.valueAt(i));
}
// Lastly, call to the icon policy to install/update all the icons.
//PhoneStatusBarPolicy mIconPolicy 初始化状态栏图标
mIconPolicy.init();
mKeyguardStateController.addCallback(this);
startKeyguard();
...
}
注册CommandQueue到StatusBarManagerService,同时返回服务端保存的图标列表
//frameworks/base/services/core/java/com/android/server/statusbar/StatusBarManagerService.java)
public RegisterStatusBarResult registerStatusBar(IStatusBar bar) {
enforceStatusBarService();
Slog.i(TAG, "registerStatusBar bar=" + bar);
mBar = bar;
mDeathRecipient.linkToDeath();
notifyBarAttachChanged();
final ArrayMap<String, StatusBarIcon> icons;
synchronized (mIcons) {
icons = new ArrayMap<>(mIcons);
}
synchronized (mLock) {
// TODO(b/118592525): Currently, status bar only works on the default display.
// Make it aware of multi-display if needed.
final UiState state = mDisplayUiState.get(DEFAULT_DISPLAY);
final int[] transientBarTypes = new int[state.mTransientBarTypes.size()];
for (int i = 0; i < transientBarTypes.length; i++) {
transientBarTypes[i] = state.mTransientBarTypes.valueAt(i);
}
return new RegisterStatusBarResult(icons, gatherDisableActionsLocked(mCurrentUserId, 1),
state.mAppearance, state.mAppearanceRegions, state.mImeWindowVis,
state.mImeBackDisposition, state.mShowImeSwitcher,
gatherDisableActionsLocked(mCurrentUserId, 2), state.mImeToken,
state.mNavbarColorManagedByIme, state.mBehavior, state.mFullscreen,
transientBarTypes);
}
}
PhoneStatusBarPolicy初始化状态栏图标入口
PhoneStatusBarPolicy:决定了哪些图标应在开机时加载到状态栏。它会根据系统状态(如蓝牙是否开启、是否有闹钟等)来显示和隐藏系统图标。StatusBar:SystemUI的主入口,负责初始化整体布局并实例。化PhoneStatusBarPolicy。StatusBarManagerService:系统服务,负责接收并管理来自各个应用的图标更新请求。IconManager:实际负责将StatusBarIconView添加到状态栏布局中。StatusBarIconView:负责单个图标的具体显示和更。StatusBarIconControllerImpl:作为中间层,将PhoneStatusBarPolicy的控制指令分发到具体的IconManager去执行。
//frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
public void init() {
// listen for broadcasts
IntentFilter filter = new IntentFilter();
filter.addAction(AudioManager.ACTION_HEADSET_PLUG);
filter.addAction(Intent.ACTION_SIM_STATE_CHANGED);
filter.addAction(TelecomManager.ACTION_CURRENT_TTY_MODE_CHANGED);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
mBroadcastDispatcher.registerReceiverWithHandler(mIntentReceiver, filter, mHandler);
Observer<Integer> observer = ringer -> mHandler.post(this::updateVolumeZen);
mRingerModeTracker.getRingerMode().observeForever(observer);
mRingerModeTracker.getRingerModeInternal().observeForever(observer);
// listen for user / profile change.
try {
mIActivityManager.registerUserSwitchObserver(mUserSwitchListener, TAG);
} catch (RemoteException e) {
// Ignore
}
// TTY status
updateTTY();
// bluetooth status
updateBluetooth();
// Alarm clock
//private final StatusBarIconController mIconController;
//mIconController在构造函数中创建,创建的时候就会去加载配置的系统状态栏图标
//StatusBarIconController实现类StatusBarIconControllerImpl
//设置图标
mIconController.setIcon(mSlotAlarmClock, R.drawable.stat_sys_alarm, null);
mIconController.setIconVisibility(mSlotAlarmClock, false);
// zen
mIconController.setIcon(mSlotZen, R.drawable.stat_sys_dnd, null);
mIconController.setIconVisibility(mSlotZen, false);
// vibrate
mIconController.setIcon(mSlotVibrate, R.drawable.stat_sys_ringer_vibrate,
mResources.getString(R.string.accessibility_ringer_vibrate));
mIconController.setIconVisibility(mSlotVibrate, false);
// mute
mIconController.setIcon(mSlotMute, R.drawable.stat_sys_ringer_silent,
mResources.getString(R.string.accessibility_ringer_silent));
mIconController.setIconVisibility(mSlotMute, false);
updateVolumeZen();
// cast
mIconController.setIcon(mSlotCast, R.drawable.stat_sys_cast, null);
mIconController.setIconVisibility(mSlotCast, false);
// hotspot
mIconController.setIcon(mSlotHotspot, R.drawable.stat_sys_hotspot,
mResources.getString(R.string.accessibility_status_bar_hotspot));
mIconController.setIconVisibility(mSlotHotspot, mHotspot.isHotspotEnabled());
// managed profile
mIconController.setIcon(mSlotManagedProfile, R.drawable.stat_sys_managed_profile_status,
mResources.getString(R.string.accessibility_managed_profile));
mIconController.setIconVisibility(mSlotManagedProfile, mManagedProfileIconVisible);
// data saver
mIconController.setIcon(mSlotDataSaver, R.drawable.stat_sys_data_saver,
mResources.getString(R.string.accessibility_data_saver_on));
mIconController.setIconVisibility(mSlotDataSaver, false);
// privacy items
String microphoneString = mResources.getString(PrivacyType.TYPE_MICROPHONE.getNameId());
String microphoneDesc = mResources.getString(
R.string.ongoing_privacy_chip_content_multiple_apps, microphoneString);
mIconController.setIcon(mSlotMicrophone, PrivacyType.TYPE_MICROPHONE.getIconId(),
microphoneDesc);
mIconController.setIconVisibility(mSlotMicrophone, false);
String cameraString = mResources.getString(PrivacyType.TYPE_CAMERA.getNameId());
String cameraDesc = mResources.getString(
R.string.ongoing_privacy_chip_content_multiple_apps, cameraString);
mIconController.setIcon(mSlotCamera, PrivacyType.TYPE_CAMERA.getIconId(),
cameraDesc);
mIconController.setIconVisibility(mSlotCamera, false);
mIconController.setIcon(mSlotLocation, LOCATION_STATUS_ICON_ID,
mResources.getString(R.string.accessibility_location_active));
mIconController.setIconVisibility(mSlotLocation, false);
// sensors off
mIconController.setIcon(mSlotSensorsOff, R.drawable.stat_sys_sensors_off,
mResources.getString(R.string.accessibility_sensors_off_active));
mIconController.setIconVisibility(mSlotSensorsOff,
mSensorPrivacyController.isSensorPrivacyEnabled());
// screen record
mIconController.setIcon(mSlotScreenRecord, R.drawable.stat_sys_screen_record, null);
mIconController.setIconVisibility(mSlotScreenRecord, false);
mRotationLockController.addCallback(this);
mBluetooth.addCallback(this);
mProvisionedController.addCallback(this);
mZenController.addCallback(this);
mCast.addCallback(mCastCallback);
mHotspot.addCallback(mHotspotCallback);
mNextAlarmController.addCallback(mNextAlarmCallback);
mDataSaver.addCallback(this);
mKeyguardStateController.addCallback(this);
mPrivacyItemController.addCallback(this);
mSensorPrivacyController.addCallback(mSensorPrivacyListener);
mLocationController.addCallback(this);
mRecordingController.addCallback(this);
mCommandQueue.addCallback(this);
}
调用StatusBarIconControllerImpl设置状态栏图标资源,名字
//frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java)
public void setIcon(String slot, int resourceId, CharSequence contentDescription) {
int index = getSlotIndex(slot);
StatusBarIconHolder holder = getIcon(index, 0);
if (holder == null) {
StatusBarIcon icon = new StatusBarIcon(UserHandle.SYSTEM, mContext.getPackageName(),
Icon.createWithResource(
mContext, resourceId), 0, 0, contentDescription);
holder = StatusBarIconHolder.fromIcon(icon);
setIcon(index, holder);
} else {
holder.getIcon().icon = Icon.createWithResource(mContext, resourceId);
holder.getIcon().contentDescription = contentDescription;
handleSet(index, holder);
}
}
public void setIcon(int index, @NonNull StatusBarIconHolder holder) {
boolean isNew = getIcon(index, holder.getTag()) == null;
super.setIcon(index, holder);
if (isNew) {
addSystemIcon(index, holder);
} else {
handleSet(index, holder);
}
}
private void addSystemIcon(int index, StatusBarIconHolder holder) {
String slot = getSlotName(index);
int viewIndex = getViewIndex(index, holder.getTag());
boolean hidden = mIconHideList.contains(slot);
//private final ArrayList<IconManager> mIconGroups = new ArrayList<>();
//回调StatusBarIconController中的IconManager的onIconAdded()
mIconGroups.forEach(l -> l.onIconAdded(viewIndex, slot, hidden, holder));
}
private void handleSet(int index, StatusBarIconHolder holder) {
int viewIndex = getViewIndex(index, holder.getTag());
//private final ArrayList<IconManager> mIconGroups = new ArrayList<>();
//回调StatusBarIconController中的IconManager的onSetIconHolder()
mIconGroups.forEach(l -> l.onSetIconHolder(viewIndex, holder));
}
//frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java)
protected void onIconAdded(int index, String slot, boolean blocked,
StatusBarIconHolder holder) {
addHolder(index, slot, blocked, holder);
}
protected StatusIconDisplayable addHolder(int index, String slot, boolean blocked,
StatusBarIconHolder holder) {
// This is a little hacky, and probably regrettable, but just set `blocked` on any icon
// that is in our blocked list, then we'll never see it
if (mBlockList.contains(slot)) {
blocked = true;
}
switch (holder.getType()) {
case TYPE_ICON:
return addIcon(index, slot, blocked, holder.getIcon());
case TYPE_WIFI:
return addSignalIcon(index, slot, holder.getWifiState());
case TYPE_MOBILE:
return addMobileIcon(index, slot, holder.getMobileState());
}
return null;
}
public void onSetIconHolder(int viewIndex, StatusBarIconHolder holder) {
switch (holder.getType()) {
case TYPE_ICON:
onSetIcon(viewIndex, holder.getIcon());
return;
case TYPE_WIFI:
onSetSignalIcon(viewIndex, holder.getWifiState());
return;
case TYPE_MOBILE:
onSetMobileIcon(viewIndex, holder.getMobileState());
default:
break;
}
}
@VisibleForTesting
protected StatusBarIconView addIcon(int index, String slot, boolean blocked,
StatusBarIcon icon) {
StatusBarIconView view = onCreateStatusBarIconView(slot, blocked);
view.set(icon);
mGroup.addView(view, index, onCreateLayoutParams());
return view;
}
@VisibleForTesting
protected StatusBarWifiView addSignalIcon(int index, String slot, WifiIconState state) {
StatusBarWifiView view = onCreateStatusBarWifiView(slot);
view.applyWifiState(state);
mGroup.addView(view, index, onCreateLayoutParams());
if (mIsInDemoMode) {
mDemoStatusIcons.addDemoWifiView(state);
}
return view;
}
状态栏图标列表配置在config.xml 中的 config_statusBarIcons 数组,在构造函数中执行初始化,StatusBar构造函数中会创建StatusBarIconController对象。
//frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java)
public StatusBarIconControllerImpl(
Context context,
CommandQueue commandQueue,
DemoModeController demoModeController) {
super(context.getResources().getStringArray(
com.android.internal.R.array.config_statusBarIcons));
Dependency.get(ConfigurationController.class).addCallback(this);
mContext = context;
loadDimens();
commandQueue.addCallback(this);
Dependency.get(TunerService.class).addTunable(this, ICON_HIDE_LIST);
demoModeController.addCallback(this);
}
创建状态栏,通知栏,导航栏
public void createAndAddWindows(@Nullable RegisterStatusBarResult result) {
//创建状态栏,导航栏,通知栏View
makeStatusBarView(result);
//通过WindowManager.addView 添加通知栏窗口
mNotificationShadeWindowController.attach();
//通过WindowManager.addView 添加状态栏窗口
mStatusBarWindowController.attach(); }
创建状态栏,导航栏,通知栏View
protected void makeStatusBarView(@Nullable RegisterStatusBarResult result) {
//创建状态栏,通知栏View
inflateStatusBarWindow();
mNotificationShadeWindowViewController.setService(this, mNotificationShadeWindowController);
...
//通知图标区域控制器
mNotificationIconAreaController.setupShelf(mNotificationShelfController);
//通知面板控制器,管理通知面板(下拉状态栏)的显示、交互与动画逻辑
mNotificationPanelViewController.addExpansionListener(mWakeUpCoordinator);
mNotificationPanelViewController.addExpansionListener(
this::dispatchPanelExpansionForKeyguardDismiss);
//通过FragmentHostManager添加CollapsedStatusBarFragment加载状态栏布局
FragmentHostManager.get(mPhoneStatusBarWindow)
.addTagListener(CollapsedStatusBarFragment.TAG, (tag, fragment) -> {
CollapsedStatusBarFragment statusBarFragment =
(CollapsedStatusBarFragment) fragment;
PhoneStatusBarView oldStatusBarView = mStatusBarView;
mStatusBarView = (PhoneStatusBarView) statusBarFragment.getView();
mStatusBarView.setBar(this);
mStatusBarView.setPanel(mNotificationPanelViewController);
mStatusBarView.setScrimController(mScrimController);
mStatusBarView.setExpansionChangedListeners(mExpansionChangedListeners);
// status by calling mNotificationPanel.notifyBarPanelExpansionChanged().
if (mHeadsUpManager.hasPinnedHeadsUp()) {
mNotificationPanelViewController.notifyBarPanelExpansionChanged();
}
mStatusBarView.setBouncerShowing(mBouncerShowing);
if (oldStatusBarView != null) {
float fraction = oldStatusBarView.getExpansionFraction();
boolean expanded = oldStatusBarView.isExpanded();
mStatusBarView.panelExpansionChanged(fraction, expanded);
}
...
mNotificationShadeWindowViewController.setStatusBarView(mStatusBarView);
checkBarModes();
}).getFragmentManager()
.beginTransaction()
.replace(R.id.status_bar_container,
new CollapsedStatusBarFragment(
mOngoingCallController,
mAnimationScheduler,
mStatusBarLocationPublisher,
mNotificationIconAreaController,
mFeatureFlags,
mStatusBarIconController,
mKeyguardStateController,
mNetworkController,
mStatusBarStateController,
this,
mCommandQueue
),
CollapsedStatusBarFragment.TAG)
.commit();
...
mNotificationPanelViewController.setHeadsUpManager(mHeadsUpManager);
//创建导航栏View
createNavigationBar(result);
...
// Set up the quick settings tile panel
//下拉通知栏的QS面板加载
final View container = mNotificationShadeWindowView.findViewById(R.id.qs_frame);
if (container != null) {
FragmentHostManager fragmentHostManager = FragmentHostManager.get(container);
ExtensionFragmentListener.attachExtensonToFragment(container, QS.TAG, R.id.qs_frame,
mExtensionController
.newExtension(QS.class)
.withPlugin(QS.class)
//创建一个默认的QSFragment实例,QSFragment会加载qs_panel.xml
.withDefault(this::createDefaultQSFragment)
.build());
mBrightnessMirrorController = new BrightnessMirrorController(
mNotificationShadeWindowView,
mNotificationPanelViewController,
mNotificationShadeDepthControllerLazy.get(),
mBrightnessSliderFactory,
(visible) -> {
mBrightnessMirrorVisible = visible;
updateScrimController();
});
fragmentHostManager.addTagListener(QS.TAG, (tag, f) -> {
QS qs = (QS) f;
if (qs instanceof QSFragment) {
mQSPanelController = ((QSFragment) qs).getQSPanelController();
//初始化亮度滑块,拖动亮度滑块时,在通知面板顶部会有一个“镜像”动画效果 mQSPanelController.setBrightnessMirror(mBrightnessMirrorController);
}
});
}
...
}
创建状态栏,通知栏View
private void inflateStatusBarWindow() {
//构建通知栏View
mNotificationShadeWindowView = mSuperStatusBarViewFactory.getNotificationShadeWindowView();
StatusBarComponent statusBarComponent = mStatusBarComponentBuilder.get()
.statusBarWindowView(mNotificationShadeWindowView).build();
mNotificationShadeWindowViewController = statusBarComponent
.getNotificationShadeWindowViewController();
mNotificationShadeWindowController.setNotificationShadeView(mNotificationShadeWindowView);
mNotificationShadeWindowViewController.setupExpandedStatusBar();
mStatusBarWindowController = statusBarComponent.getStatusBarWindowController();
////构建状态栏View
mPhoneStatusBarWindow = mSuperStatusBarViewFactory.getStatusBarWindowView();
mNotificationPanelViewController = statusBarComponent.getNotificationPanelViewController();
statusBarComponent.getLockIconViewController().init();
}
加载根布局super_notification_shade.xml,构建通知栏界面布局
NotificationShadeWindowView 的布局非常复杂,是整个下拉通知栏界面的根视图,它像一个容器,组合了多个关键子视图来实现丰富的功能。其主要组成部分包括:
-
背景与遮罩层:负责视觉层次和过渡效果。
BackDropView(@+id/backdrop) :用于显示通知或媒体应用的背景 artwork,在展开通知或播放音乐时提供沉浸式视觉背景。ScrimView(@+id/scrim_behind) :位于通知面板后方的遮罩层,用于在拉下通知栏时,将桌面或应用界面变暗,突出前景内容。ScrimView(@+id/scrim_notifications) :位于通知列表自身的遮罩层,用于在滚动通知列表时,在列表顶部或底部创建渐隐效果。
-
核心功能面板:承载了用户交互的主要内容。
LightRevealScrim(@+id/light_reveal_scrim) :一个特殊的遮罩,用于实现锁屏与AOD(Always On Display,息屏显示)之间的灯光揭示动画,创造平滑的视觉过渡效果。<include layout="@layout/status_bar_expanded"/>:这是通知栏的核心,它包含了通知列表、快捷设置面板(QS Panel)等所有主要UI。默认是invisible,只有在下拉时才会变为可见。<include layout="@layout/brightness_mirror_container"/>:用于在快捷设置面板中显示亮度滑块的镜像布局。
-
锁屏与安全相关组件:
KeyguardMessageArea(@+id/keyguard_message_area) :在锁屏界面显示提示信息,如“滑动解锁”或充电状态等。AuthRippleView(@+id/auth_ripple) :用于指纹或面部识别认证时的视觉反馈,会在认证成功或失败时产生波纹动画。
-
前景遮罩层:
ScrimView(@+id/scrim_in_front) :位于所有内容最前方的遮罩层,用于在显示锁屏验证界面(Bouncer) 时,模糊或遮盖后面的通知内容,保护隐私。
核心作用:通知面板与锁屏的载体
- 通知面板的根视图:它是整个下拉通知栏的顶层容器。所有你下拉后看到的元素——通知列表、快捷开关、亮度滑块等,都作为子View包含在其中。
- 锁屏界面的载体:与常规认知不同,锁屏界面(Keyguard)并非一个独立窗口,而是与通知面板共用
NotificationShadeWindowView。这意味着锁屏上的时钟、消息、解锁提示等,都是这个窗口的一部分。 - 系统事件与交互的处理枢纽:它作为根视图,是触摸事件、按键事件的分发起点。例如,在AOD状态下双击屏幕,便是由它来捕获并唤醒设备的
//frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/SuperStatusBarViewFactory.java
public NotificationShadeWindowView getNotificationShadeWindowView() {
if (mNotificationShadeWindowView != null) {
return mNotificationShadeWindowView;
}
mNotificationShadeWindowView = (NotificationShadeWindowView)
mInjectionInflationController.injectable(
LayoutInflater.from(mContext)).inflate(R.layout.super_notification_shade,
/* root= */ null);
if (mNotificationShadeWindowView == null) {
throw new IllegalStateException(
"R.layout.super_notification_shade could not be properly inflated");
}
return mNotificationShadeWindowView;
}
//frameworks/base/packages/SystemUI/res/layout/super_notification_shade.xml)
<com.android.systemui.statusbar.phone.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.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.scrim.ScrimView
android:id="@+id/scrim_behind"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:importantForAccessibility="no"
sysui:ignoreRightInset="true"
/>
<com.android.systemui.scrim.ScrimView
android:id="@+id/scrim_notifications"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:importantForAccessibility="no"
sysui:ignoreRightInset="true"
/>
<com.android.systemui.statusbar.LightRevealScrim
android:id="@+id/light_reveal_scrim"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone" />
//通知面板
<include layout="@layout/status_bar_expanded"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="invisible" />
//亮度滑块
<include layout="@layout/brightness_mirror_container" />
<com.android.systemui.scrim.ScrimView
android:id="@+id/scrim_in_front"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:importantForAccessibility="no"
sysui:ignoreRightInset="true"
/>
<!-- Keyguard messages -->
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/status_bar_height"
android:layout_gravity="top|center_horizontal"
android:gravity="center_horizontal">
<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" />
</FrameLayout>
<com.android.systemui.biometrics.AuthRippleView
android:id="@+id/auth_ripple"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:importantForAccessibility="no"
sysui:ignoreRightInset="true"
/>
</com.android.systemui.statusbar.phone.NotificationShadeWindowView>
super_status_bar.xml,是状态栏根布局,
StatusBarWindowView 是状态栏UI的顶层容器,负责承载和显示状态栏相关的所有视觉元素
。status_bar.xml是状态栏实际的布局 ,顶层容器是 PhoneStatusBarView(继承自 FrameLayout)。它的内部主要分为三个区域:
-
左侧区域 (
status_bar_left_side) :Clock:显示时间的视图。notification_icon_area:显示应用通知图标(如微信、短信图标)的容器。这些图标由NotificationIconAreaController动态填充。ongoing_call_chip:正在进行的通话提示条。
-
中间区域 (
centered_icon_area) :- 主要用于适配有刘海或挖孔的屏幕,在特定场景下(如自拍相机开启时)显示提示光点或摄像头指示器。
-
右侧区域 (
system_icon_area) :- 包含
system_icons布局,里面是信号强度、Wi-Fi、电池电量等系统级图标,由StatusBarIconController管理。
- 包含
-
其他元素:
cutout_space_view:专门用来占据屏幕挖孔/刘海的位置,防止其他视图被遮挡。emergency_cryptkeeper_text:加密状态或紧急情况下的文字提示(通常为ViewStub,按需加载)。
//frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/SuperStatusBarViewFactory.java
public StatusBarWindowView getStatusBarWindowView() {
if (mStatusBarWindowView != null) {
return mStatusBarWindowView;
}
mStatusBarWindowView =
(StatusBarWindowView) mInjectionInflationController.injectable(
LayoutInflater.from(mContext)).inflate(R.layout.super_status_bar,
/* root= */ null);
if (mStatusBarWindowView == null) {
throw new IllegalStateException(
"R.layout.super_status_bar could not be properly inflated");
}
return mStatusBarWindowView;
}
//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">
<FrameLayout
//用于处理应用启动动画
android:id="@+id/status_bar_launch_animation_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<FrameLayout
//用于加载真正的状态栏
android:id="@+id/status_bar_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/system_bar_background" />
</com.android.systemui.statusbar.phone.StatusBarWindowView>
//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: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: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" />
<!-- The alpha of the left side is controlled by PhoneStatusBarTransitions, and the
individual views are controlled by StatusBarManager disable flags DISABLE_CLOCK and
DISABLE_NOTIFICATION_ICONS, respectively -->
<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"
/>
//正在通话提示条
<include layout="@layout/ongoing_call_chip" />
<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>
<!-- Space should cover the notch (if it exists) and let other views lay out around it -->
<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.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"/>
<com.android.keyguard.AlphaOptimizedLinearLayout
//右侧系统图标区域:信号强度、Wi-Fi、电池电量等系统级图标
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>
通过WindowManager.addView 添加通知栏窗口
//frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java)
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.softInputMode = LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
mLp.setTitle("NotificationShade");
mLp.packageName = mContext.getPackageName();
mLp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
// We use BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE here, however, there is special logic in
// window manager which disables the transient show behavior.
// TODO: Clean this up once that behavior moves into the Shell.
mLp.privateFlags |= PRIVATE_FLAG_BEHAVIOR_CONTROLLED;
mLp.insetsFlags.behavior = BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
mWindowManager.addView(mNotificationShadeView, mLp);
mLpChanged.copyFrom(mLp);
onThemeChanged();
// Make the state consistent with KeyguardViewMediator#setupLocked during initialization.
if (mKeyguardViewMediator.isShowingAndNotOccluded()) {
setKeyguardShowing(true);
}
}
通过WindowManager.addView 添加状态栏窗口
//frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java)
public void attach() {
mLp = new WindowManager.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
mBarHeight,
WindowManager.LayoutParams.TYPE_STATUS_BAR,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
| WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
PixelFormat.TRANSLUCENT);
mLp.privateFlags |= PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC;
mLp.token = new Binder();
mLp.gravity = Gravity.TOP;
mLp.setFitInsetsTypes(0 /* types */);
mLp.setTitle("StatusBar");
mLp.packageName = mContext.getPackageName();
mLp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
mWindowManager.addView(mStatusBarView, mLp);
mLpChanged.copyFrom(mLp);
mContentInsetsProvider.addCallback(this::calculateStatusBarLocationsForAllRotations);
calculateStatusBarLocationsForAllRotations();
}
CollapsedStatusBarFragment整体结构:
StatusBar(大总管) :StatusBar在makeStatusBarView()方法中创建并添加CollapsedStatusBarFragment到布局容器中。StatusBarIconController(中央调度室) :CollapsedStatusBarFragment会向它注册DarkIconManager,从而接收图标更新指令。PhoneStatusBarPolicy(策略制定者) :它不直接与CollapsedStatusBarFragment交互,而是通过StatusBarIconController下发指令,最终由CollapsedStatusBarFragment中的DarkIconManager执行。
生命周期与关键方法
onCreateView():在Fragment创建视图时调用,核心工作是加载status_bar.xml布局文件。onViewCreated():在视图创建完成后调用,会找到status_bar.xml中定义的各个视图元素(如mSystemIconArea、mClockView等)。initNotificationIconArea():专门用于初始化左侧通知图标区域的方法。showSystemIconArea()/hideSystemIconArea():提供直接控制系统图标显示或隐藏的方法。disable():作为CommandQueue.Callbacks接口的方法,用于接收系统“禁用”标志,并执行相应逻辑。
//frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java)
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.status_bar, container, false);
}
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mStatusBar = (PhoneStatusBarView) view;
View contents = mStatusBar.findViewById(R.id.status_bar_contents);
contents.addOnLayoutChangeListener(mStatusBarLayoutListener);
updateStatusBarLocation(contents.getLeft(), contents.getRight());
...
//DarkIconManager实现了IconManager
mDarkIconManager = new DarkIconManager(view.findViewById(R.id.statusIcons), mFeatureFlags);
mDarkIconManager.setShouldLog(true);
mBlockedIcons.add(getString(com.android.internal.R.string.status_bar_volume));
mBlockedIcons.add(getString(com.android.internal.R.string.status_bar_alarm_clock));
mBlockedIcons.add(getString(com.android.internal.R.string.status_bar_call_strength));
mDarkIconManager.setBlockList(mBlockedIcons);
//
mStatusBarIconController.addIconGroup(mDarkIconManager);
mSystemIconArea = mStatusBar.findViewById(R.id.system_icon_area);
mClockView = mStatusBar.findViewById(R.id.clock);
mOngoingCallChip = mStatusBar.findViewById(R.id.ongoing_call_chip);
showSystemIconArea(false);
showClock(false);
initEmergencyCryptkeeperText();
initOperatorName();
initNotificationIconArea();
mAnimationScheduler.addCallback(this);
}
//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: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: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" />
<!-- The alpha of the left side is controlled by PhoneStatusBarTransitions, and the
individual views are controlled by StatusBarManager disable flags DISABLE_CLOCK and
DISABLE_NOTIFICATION_ICONS, respectively -->
<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"
/>
<include layout="@layout/ongoing_call_chip" />
<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>
<!-- Space should cover the notch (if it exists) and let other views lay out around it -->
<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.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"/>
<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>
PhoneStatusBarView 承担着多重职责:
- 状态栏的UI容器:它是状态状态栏所有UI元素的直接父容器。
- 交互事件的处理者:它重写了父类
PanelBar的方法(如onPanelPeeked()),负责处理用户的触摸事件,并触发相应的逻辑(如下拉展开通知面板)。 - 与控制器(
StatusBar)的桥梁:它持有StatusBar的引用(mBar),作为视图层与控制器层通信的桥梁。例如,当面板被拖拽时,它会调用mBar.makeExpandedVisible()来显示展开后的通知面板。
与其他核心组件的关系
PhoneStatusBarView 在SystemUI的视图体系中,与多个组件协同工作:
StatusBar:控制器。SystemUI 的核心服务之一,负责整体状态栏的逻辑控制。PhoneStatusBarView通过setBar()方法与之关联,两者互相持有引用。StatusBarWindowView:根布局容器。它是整个状态栏窗口的根视图。PhoneStatusBarView作为其子视图存在。NotificationShadeWindowView:通知面板(展开状态)的根视图。它与StatusBarWindowView是平级关系,是两个独立的系统窗口。PhoneStatusBarView负责触发它的显示(如下拉时)。NotificationPanelView:通知面板的具体内容视图。CollapsedStatusBarFragment:UI加载器。这个Fragment负责将status_bar.xml布局文件(即PhoneStatusBarView)加载到StatusBarWindowView中。