本文将比较全面的介绍SystemUI的组成,实现,布局以及客制化方案等等。本博文基于android8.0源码进行分析,由于android8.0相比以前的版本SystemUI部分改动是非常大的。
一、SystemUI组成
SystemUI包含的功能非常丰富,组成元素主要包含常见的System Bars,以及ScreenShot截屏、壁纸、最近运行的应用程序等。SystemUI也是各大安卓版本中变化比较大的一个部分。
Status Bar
Navigation Bar
Combined Bar (主要为Tablet设备使用)
Notifications
LockScreen
Recent (最近任务)
QuickSettings
等等
二、SystemUI实现
了解了SystemUI后,本文先来大概讲解下StatusBar的实现流程。
1、应用启动相关代码
相关代码主要分为两个部分
1)、Service部分
代码路径:framework/base/services/java/com/android/server
2) 、应用部分
代码路径:framework/base/packages/SystemUI
SystemUI中SystemUIService是整个系统UI比较重要的载体,所以我们的分析将从SystemUIService开始,而SystemUIService是从SystemServer中启动的。关于这部分这里不做多的分析,详见SystemServer.java中的startSystemUi()方法。下面来看一下SystemUIServer中的onCreate()方法。
/*framework/base/packages/systemui/src/com/android/systemui/SystemUIService.java*/
@Override
public void onCreate() {
super.onCreate();
((SystemUIApplication) getApplication()).startServicesIfNeeded();
// For debugging RescueParty
if (Build.IS_DEBUGGABLE && SystemProperties.getBoolean("debug.crash_sysui", false)) {
throw new RuntimeException();
}
}
上面的代码可以看出SystemUIService中实际有效代码只是实例化了一个SystemUIApplication对象,并且调用了startServiceIfNeeded()方法。下面来看SystemUIApplication中的具体实现逻辑。
public void startServicesIfNeeded() {
startServicesIfNeeded(SERVICES);
}
其中SERVICES是一组所有用户共用的SystemUI服务,如下:
/**
* The classes of the stuff to start.
*/
private final Class>[] SERVICES = new Class[] {
Dependency.class,
NotificationChannels.class, //通知管理
CommandQueue.CommandQueueStart.class,
KeyguardViewMediator.class, //锁屏管理
Recents.class, //近期应用管理,以堆叠栈的形式展现
VolumeUI.class, //用来展示和控制音量的变化:媒体音量、铃声音量和闹钟音量
Divider.class, //分屏管理
SystemBars.class,
StorageNotification.class,
PowerUI.class, //主要处理和Power相关的事件,比如省电模式切换,电池电量变化和开关屏幕等事件
RingtonePlayer.class, //铃声播放
KeyboardUI.class,
PipUI.class, //提供对画中画模式的管理
ShortcutKeyDispatcher.class, //
VendorServices.class,
GarbageMonitor.Service.class,
LatencyTester.class,
GlobalActionsComponent.class,
RoundedCorners.class,
};
/**
* The classes of the stuff to start for each user. This is a subset of the services listed
* above.
*/
private final Class>[] SERVICES_PER_USER = new Class[] {
Dependency.class,
NotificationChannels.class,
Recents.class
};
上面对SystemUI要启动的一系列服务有了个基本的介绍,下面来看SystemUIApplication中是怎么启动这一些列服务的
private void startServicesIfNeeded(Class>[] services) {
...
log.traceBegin("StartServices");
final int N = services.length; //获取要启动的服务列表的长度
for (int i = 0; i < N; i++) {
Class> cl = services[i];
if (DEBUG) Log.d(TAG, "loading: " + cl);
log.traceBegin("StartServices" + cl.getSimpleName());
long ti = System.currentTimeMillis();
try {
/* 通过SystemUIFactory来创建相应的单例 */
Object newService = SystemUIFactory.getInstance().createInstance(cl);
mServices[i] = (SystemUI) ((newService == null) ? cl.newInstance() : newService);
}...
mServices[i].mContext = this;
mServices[i].mComponents = mComponents;
if (DEBUG) Log.d(TAG, "running: " + mServices[i]);
mServices[i].start(); //服务启动的地方
log.traceEnd();
// Warn if initialization of component takes too long
ti = System.currentTimeMillis() - ti;
if (ti > 1000) {
Log.w(TAG, "Initialization of " + cl.getName() + " took " + ti + " ms");
}
if (mBootCompleted) {
mServices[i].onBootCompleted();
}
}
log .traceEnd(); ...
}
可以看到SystemBar这个服务只是作为了一个中间过程,启动了StatusBar,现在我们开看一下StatusBar的start()方法:
@Override
public void start() {
... //这里面进行了StatusBar中各个组件的初始化
mBarService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
...
try {
/* 经过一系列对象的创建与初始化后,开始向StatusBarService进行注册。这里涉及跨进程操作,
因而传递的参数都是继承自Parcelable的 */
mBarService.registerStatusBar(mCommandQueue, iconSlots, icons, switches, binders,
fullscreenStackBounds, dockedStackBounds);
} ...
createAndAddWindows(); //这里才是真正将Status Bar显示出来的地方
}
这里大家就会有疑问了,既然已经有了StatusBar了,那么这里突然杀出来个StatusBarService,到底是为什么呢?
先来看看StatusBarService,通过Context.STATUS_BAR_SERVICE,直觉告诉我们这个应用程序应该是在SystemServer中。我们可以看看是谁向SystemServer中注册的这个服务,下面来看一下SystemUI中的代码。
if (!disableSystemUI) {
traceBeginAndSlog("StartStatusBarManagerService");
try {
statusBar = new StatusBarManagerService(context, wm);
ServiceManager.addService(Context.STATUS_BAR_SERVICE, statusBar);
} catch (Throwable e) {
reportWtf("starting StatusBarManagerService", e); //原来StatusBarManagerService这个家伙注册的
}
traceEnd();
}
接下来进一步分析StatusBarManagerService的实现,首先来看下其中的registerStatusBar中的代码:
@Override
public void registerStatusBar(IStatusBar bar, List
List
Rect fullscreenStackBounds, Rect dockedStackBounds) {
enforceStatusBarService();
Slog.i(TAG, "registerStatusBar bar=" + bar);
mBar = bar;
try {
mBar.asBinder().linkToDeath(new DeathRecipient() {
@Override
public void binderDied() {
mBar = null;
notifyBarAttachChanged();
}
}, 0);
} catch (RemoteException e) {
}
notifyBarAttachChanged();
synchronized (mIcons) { //复制icon列表
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;
switches[3] = mImeWindowVis;
switches[4] = mImeBackDisposition;
switches[5] = mShowImeSwitcher ? 1 : 0;
switches[6] = gatherDisableActionsLocked(mCurrentUserId, 2);
switches[7] = mFullscreenStackSysUiVisibility;
switches[8] = mDockedStackSysUiVisibility;
binders.add(mImeToken);
fullscreenStackBounds.set(mFullscreenStackBounds);
dockedStackBounds.set(mDockedStackBounds);
}
}
从上面的代码看,registerStatusBar的作用主要是:一、为新启动的SystemUI应用赋予当前系统的真实值(比如有多少需要显示的图标);二、通过成员变量mBar记录IstatusBar对象,它在SystemUI中对应的是CommandQueue。
下面通过自己整理的调用流程图,大家可以参考一下: