SystemUI 系统状态栏 StatusBar 分析(AndroidO)
1 概述
在 SystemUI 中,状态栏为用户提供一些快捷的信息显示和功能操作。在一些小屏幕设备上状态栏可以通过下拉的方式为用户显示更多必要的系统信息,以达到对屏幕空间合理的利用。在 848 平台上,状态栏有 StatusBar 和 TvStatusBar 两种可配置的状态栏实现,默认配置使用的是适合小设备的 StatusBar 状态栏,但平台并没有使用到状态栏。这里主要分析常见的小屏设备(手机) StatusBar 状态栏的创建和启动相关流程。
2 状态栏分析
2.1 状态栏启动
负责启动状态栏组件服务的是 SystemBars,它位于 SystemUIApplication 类中维护的 SERVICES 数组常量服务列表中,状态栏主要是通过调用 SystemBars.start() 方法来启动的。
在状态栏启动之前,会先获取 config.xml 配置文件创建 StatusBar 实例,然后再调用 StatusBar.start() 方法进行状态栏的创建相关工作。这里可通过修改 config.xml 文件中配置自定义 StatusBar。
// frameworks\base\packages\SystemUI\res\values\config.xml
<!-- 可自定义 StatusBar,也可替换配置,如 com.android.systemui.statusbar.tv.TvStatusBar -->
<string name="config_statusBarComponent" translatable="false">com.android.systemui.statusbar.phone.StatusBar</string>
// frameworks\base\packages\SystemUI\src\com\android\systemui\SystemBars.java
public class SystemBars extends SystemUI {
private SystemUI mStatusBar;
@Override
public void start() {
createStatusBarFromConfig();
}
private void createStatusBarFromConfig() {
// 获取上面的 config.xml 配置,根据全类名创建 StatusBar 实例
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;
cls = mContext.getClassLoader().loadClass(clsName);
mStatusBar = (SystemUI) cls.newInstance();
mStatusBar.mContext = mContext;
mStatusBar.mComponents = mComponents;
mStatusBar.start(); // 调用 StatusBar 的 start() 方法
}
...
}
StatusBar 继承于 SystemUI 这个基类,实现了很多接口,这里比较关键的接口是 CommandQueue.Callbacks, CommandQueue 继承于 IStatusBar.Stub,内部维护了一个 Callbacks 类型的数组变量,在 StatusBar.start() 中会将 StatusBar 实例添加到这个数组中,方便后续对 StatusBar 进行相关的代理调用。
在 StatusBar.start() 中不仅负责状态栏窗口的创建,还会涉及到导航栏、通知栏等相关其他处理,这里暂时只分析状态栏的相关处理。
StatusBar.start() 方法中主要做了几件事:
- 初始化 mWindowManager、mWindowManagerService 变量
- 获取 StatusBarManagerService 服务客户端代理实例,它负责接收系统服务对状态栏的请求并且将请求转发给 StatusBar,同时它保存了状态栏需要显示的相关信息的副本,这样是为了防止 SystemUI 意外崩溃时信息丢失。
- 获取 CommandQueue 实例 ,将 StatusBar 实例加入 Callbacks 数组,并将 CommandQueue 实例注册到 StatusBarManagerService,相当于在 StatusBarManagerService 实例中维护了一份 CommandQueue,然后 CommandQueue 又是继承于 IStatusBar.Stub,所以会将对状态栏的操作交给CommandQueue 来处理。
- 创建状态栏和导航栏的窗口
- 对取出的状态栏副本信息进行相关处理
// frameworks\base\packages\SystemUI\src\com\android\systemui\statusbar\phone\StatusBar.java
public class StatusBar extends SystemUI implements CommandQueue.Callbacks,... {
public void start() {
...
// 状态栏、导航栏窗口不属于任意 Activity, 而是由 WindowManager 来创建
mWindowManager = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
mDisplay = mWindowManager.getDefaultDisplay();
updateDisplaySize();
// 获取 WMS
mWindowManagerService = WindowManagerGlobal.getWindowManagerService();
// 获取 IStatusBarService 实例
mBarService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
//CommandQueue 继承于 IStatiusBar.Stub,是 IStatusBarService 与 StatusBar 通信的桥梁
mCommandQueue = getComponent(CommandQueue.class); // 在 CommandQueueStart.start() -> putComponent()
mCommandQueue.addCallbacks(this); // 将 StatusBar实例 加入 CommandQueue 的 Callbacks 数组
int[] switches = new int[9];
ArrayList<IBinder> binders = new ArrayList<IBinder>();
ArrayList<String> iconSlots = new ArrayList<>();
ArrayList<StatusBarIcon> icons = new ArrayList<>();
Rect fullscreenStackBounds = new Rect();
Rect dockedStackBounds = new Rect();
// 将 StatusBar 注册到 IStatusBarService 系统服务之中,并取出需要状态栏显示与处理的相关信息副本
// mCommandQueue 作为参数注入到 IStatusBarService 中
mBarService.registerStatusBar(mCommandQueue, iconSlots, icons, switches, binders,
fullscreenStackBounds, dockedStackBounds);
// 创建状态栏和导航栏的窗口,后面会分析
createAndAddWindows();
// 对取出的状态栏副本信息进行相关处理
int N = iconSlots.size();
int viewIndex = 0;
for (int i=0; i < N; i++) {
mCommandQueue.setIcon(iconSlots.get(i), icons.get(i));
}
...
}
}
// frameworks\base\services\core\java\com\android\server\statusbar\StatusBarManagerService.java
public void registerStatusBar(IStatusBar bar, List<String> iconSlots,
List<StatusBarIcon> iconList, int switches[], List<IBinder> binders,
Rect fullscreenStackBounds, Rect dockedStackBounds) {
enforceStatusBarService();
// 将 bar(CommandQueue) 保存在 mBar 中, mBar 后续会将一些对状态栏的操作请求转交给 mBar
mBar = bar;
...
// 将相关副本信息填充回参数中
synchronized (mIcons) {
for (String slot : mIcons.keySet()) {
iconSlots.add(slot);
iconList.add(mIcons.get(slot));
}
}
synchronized (mLock) {
switches[0] = gatherDisableActionsLocked(mCurrentUserId, 1);
switches[1] = mSystemUiVisibility;
switches[2] = mMenuVisible ? 1 : 0;
...
}
}
StatusBarManagerService 作为系统服务, 是在 SystemServer.run() -> startOtherServices() 这个阶段创建启动的。
// frameworks\base\services\java\com\android\server\SystemServer.java
private void startOtherServices() {
...
if (!disableSystemUI) {
// config.disable_systemui
try {
statusBar = new StatusBarManagerService(context, wm);
ServiceManager.addService(Context.STATUS_BAR_SERVICE, statusBar);
} catch (Throwable e) {
reportWtf("starting StatusBarManagerService", e);
}
}
...
}
那么 StatusBar 如何和 StatusBarManagerService 建立通信连接的呢?上面也已经分析,通俗来讲就是在 IStatusBarService 维护一份 CommandQueue 实例,而该实例又是继承于 IStatusBar.Stub,所以很多对状态栏的请求转发到 StatusBarManagerService 后,实际上会转交给了 CommandQueue 进行处理,也就是说 CommandQueue 相当于 IStatusBarService 与 StatusBar 通信的桥梁。再说多一点,就是 CommandQueue 接收处理的请求,会再转交给其内部维护的 Callbacks 类型的数组变量 mCallbacks 来进行相关的调用,最后才会真正调用到 StatusBar 实例中相关的方法。
这里以 StatusBarManagerService 提供的 collapsePanels() 方法的调用为例。
// frameworks\base\services\core\java\com\android\server\statusbar\StatusBarManagerService.java
private volatile IStatusBar mBar;
@Override
public void collapsePanels() {
enforceExpandStatusBar();
if (mBar != null) {
try {
mBar.animateCollapsePanels(); //
} catch (RemoteException ex) {
}
}
}
CommandQueue 类分析
// frameworks\base\packages\SystemUI\src\com\android\systemui\statusbar\CommandQueue.java
public class CommandQueue extends IStatusBar.Stub {
private ArrayList<Callbacks> mCallbacks = new ArrayList<>();
public interface Callbacks {
...
default void animateCollapsePanels(int flags) {
} //
...
default void toggleRecentApps() {
}
default void toggleSplitScreen() {
}
}
public void animateCollapsePanels() {
synchronized (mLock) {
mHandler.removeMessages(MSG_COLLAPSE_PANELS);
mHandler.obtainMessage(MSG_COLLAPSE_PANELS, 0, 0).sendToTarget();
}
}
private final class H extends Handler {
public void handleMessage(Message msg) {
final int what = msg.what & MSG_MASK;
switch (what) {
case MSG_COLLAPSE_PANELS:
for (int i = 0; i < mCallbacks.size(); i++) {
mCallbacks.get(i).animateCollapsePanels(msg.arg1);
}
break;
...
}
}
}
}
最终调用到 StatusBar.animateCollapsePanels() 实例方法