SystemUI的启动与定制化
SystemUI包含基本的StatusBar、VolumeBar、NavigationBar等部分,在手机开机时就已经为我们加载好,但是有时候会出现对StatusBar,DropList等进行定制化的任务,那么就需要了解SystemUI的启动流程,了解StatusBar,DropList等view是如何加载在系统界面上,下文是从SystemUI启动入口、SystemUI的加载机制以及以StatusBar为例来分析整个流程
一、SystemUI的启动入口
SystemUI的加载是在Android系统启动的时候,那么我们可以知道SystemUI的入口可能是在系统启动的流程中。发现在SyetemServer进程中开始启动系统服务,如AMS,PMS,蓝牙,窗口管理服务等,其中就包括SystemUI
/frameworks/base/services/java/com/android/server/SystemServer.java
在SystemServer中可以发现启动SystemServer的是zygote进程
public final class SystemServer {
…
/**
* The main entry point from zygote.
*/
public static void main(String[] args) {
new SystemServer().run();//函数入口
}
...
private void run() {
...
// Initialize native services.初始化了native services
System.loadLibrary("android_servers");
// Check whether we failed to shut down last time we tried.
// This call may not return.
performPendingShutdown();
// Initialize the system context.初始化了system context
createSystemContext();
// Create the system service manager.接着创建一个SystemServiceManager对象用于后续系统服务的启动和管理
mSystemServiceManager = new SystemServiceManager(mSystemContext);
mSystemServiceManager.setStartInfo(mRuntimeRestart,
mRuntimeStartElapsedTime, mRuntimeStartUptime);
LocalServices.addService(SystemServiceManager.class, mSystemServiceManager);
// Prepare the thread pool for init tasks that can be parallelized
SystemServerInitThreadPool.get();
// Start services.
try {
traceBeginAndSlog("StartServices");开始系统服务的启动
startBootstrapServices();启动引导service
startCoreServices();启动核心service
startOtherServices();启动其他的services
SystemServerInitThreadPool.shutdown();
} catch (Throwable ex) {
Slog.e("System", "******************************************");
Slog.e("System", "************ Failure starting system services", ex);
throw ex;
} finally {
traceEnd();
}
...
}
~startBootstrapServices()
private void startBootstrapServices() {
…
Installer installer = mSystemServiceManager.startService(Installer.class);
mSystemServiceManager.startService(DeviceIdentifiersPolicyService.class);
mActivityManagerService = mSystemServiceManager.startService(
ActivityManagerService.Lifecycle.class).getService();
mActivityManagerService.setSystemServiceManager(mSystemServiceManager);
mActivityManagerService.setInstaller(installer);
mPowerManagerService = mSystemServiceManager.startService(PowerManagerService.class);
mSystemServiceManager.startService(LightsService.class);
mSystemServiceManager.startService(new OverlayManagerService(mSystemContext, installer));
...
}
在startBootstrapServices()方法中,可以发现mSystemServiceManager.startServic()为核心所在,方法中传入不同的service作为参数,以实现不同services的开启,包括AMS,PMS,LightsService等系统所需的一些小的关键服务
~startCoreServices()
/**
* Starts some essential services that are not tangled up in the bootstrap process.
*/
private void startCoreServices() {
traceBeginAndSlog(“StartBatteryService”);
// Tracks the battery level. Requires LightService.
mSystemServiceManager.startService(BatteryService.class);
traceEnd();
// Tracks application usage stats.
traceBeginAndSlog("StartUsageService");
mSystemServiceManager.startService(UsageStatsService.class);
mActivityManagerService.setUsageStatsManager(
LocalServices.getService(UsageStatsManagerInternal.class));
traceEnd();
// Tracks whether the updatable WebView is in a ready state and watches for update installs.
if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_WEBVIEW)) {
traceBeginAndSlog("StartWebViewUpdateService");
mWebViewUpdateService = mSystemServiceManager.startService(WebViewUpdateService.class);
traceEnd();
}
...
}
同样的,代码中以mSystemServiceManager.startService()来开启服务,只不过里面的参数不同
~startOtherServices():在这个方法中启动SystemUI
private void startOtherServices() {
…
traceBeginAndSlog(“StartSystemUI”);
try {
startSystemUi(context, windowManagerF);
} catch (Throwable e) {
reportWtf(“starting System UI”, e);
}
}
~startSystemUi()
static final void startSystemUi(Context context, WindowManagerService windowManager) {
Intent intent = new Intent();
intent.setComponent(new ComponentName(“com.android.systemui”,
“com.android.systemui.SystemUIService”));
intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
//Slog.d(TAG, "Starting service: " + intent);
context.startServiceAsUser(intent, UserHandle.SYSTEM);
windowManager.onSystemUiStarted();
}
在上面代码中可以看见创建了一个Intent,然后通过设置组件名称来开启SystemUIService,至此,SystemUI才只是找到启动的入口,对于系统启动完全完成,需要进入到SystemUIService中查看详细的启动流程。
二、SystemUI开始加载
/frameworks/base/packages/SystemUI/src/com/android/systemui/SystemUIService.java
public class SystemUIService extends Service {
@Override
public void onCreate() {
super.onCreate();
((SystemUIApplication) getApplication()).startServicesIfNeeded();
...
}
}
/frameworks/base/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
public class SystemUIApplication extends Application implements SysUiServiceProvider {
/**
* The classes of the stuff to start.
*/
private final Class<?>[] SERVICES = new Class[] {
Dependency.class,//启动的第一个SystemUI服务应始终为Dependency。Dependency是用来封装SystemUI应用中的一些依赖对象。
//Dependency提供了一种静态方法,用于获取具有跨越sysui的生命周期的依赖项。
//Dependency具有如何创建手动添加的所有依赖项的代码。 SystemUIFactory还能够添加/替换这些依赖项。
NotificationChannels.class,
CommandQueue.CommandQueueStart.class,
KeyguardViewMediator.class, // 锁屏界面
Recents.class,//最近使用的Activity
VolumeUI.class,//音量相关的UI
Divider.class,// 多窗口分屏
SystemBars.class, //系统状态栏,包括 StatusBar 、 NavigationBar、下拉消息、快捷菜单等等
StorageNotification.class, //存储相关的通知
PowerUI.class, //电池电量弹窗
RingtonePlayer.class, // 铃声控制
KeyboardUI.class,
PipUI.class,
ShortcutKeyDispatcher.class,
VendorServices.class,
GarbageMonitor.Service.class,
LatencyTester.class,
GlobalActionsComponent.class,
RoundedCorners.class,
};
…
public void startServicesIfNeeded() {
startServicesIfNeeded(SERVICES);
}
}
在startServicesIfNeeded()方法中先将这些小部件集合在一起,然后调用startServicesIfNeeded(String[] services)
private void startServicesIfNeeded(String[] services) {
private SystemUI[] mServices;
…
mServices = new SystemUI[services.length];
...
final int N = services.length;
for (int i = 0; i < N; i++) {
String clsName = services[i];
Class cls;
try {
cls = Class.forName(clsName);
mServices[i] = (SystemUI) cls.newInstance();
} catch(ClassNotFoundException ex){
throw new RuntimeException(ex);
} catch (IllegalAccessException ex) {
throw new RuntimeException(ex);
} catch (InstantiationException ex) {
throw new RuntimeException(ex);
}
mServices[i].mContext = this;
mServices[i].mComponents = mComponents;
if (DEBUG) Log.d(TAG, "running: " + mServices[i]);
mServices[i].start();
...
}
}
首先创建了一个SystemUI数组,这个是用来装载systemUI上各个小部件,接着遍历了在startServicesIfNeeded()方法中获取的services数组,通过反射的方式,获取各个不同的systemUI的对象,最后分别调用他们的start()方法。例如循环第六次获取到的是VolumeUI的对象,最后便调用的是VolumeUI的start()方法。
SystemUI开始加载不同位置的UI,而每个UI内部是如何加载,如何将view放置在不同的位置上的,我们继续往下看。
三、StatusBar的加载与定制化
由于SystemUI所包含的部分很多,这里就以加载状态栏StatusBar为例。上述遍历获取到了SystemBar对象,并开始调用它的start(),那么我们就进入到SystemBar类中查看它的start()。
/frameworks/base/packages/SystemUI/src/com/android/systemui/SystemBars.java
public class SystemBars extends SystemUI {
…
@Override
public void start() {
if (DEBUG) Log.d(TAG, “start”);
createStatusBarFromConfig();
}
…
private void createStatusBarFromConfig() {
final String clsName = mContext.getString(R.string.config_statusBarComponent);
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();
if (DEBUG) Log.d(TAG, "started " + mStatusBar.getClass().getSimpleName());
}
}
start方法中调用了createStatusBarFromConfig(),接着进入到createStatusBarFromConfig中,在这里,第一眼感觉代码有点熟悉,回想一下和上面SystemUIApplication类中startServicesIfNeeded(String[] services)加载不同systemUI的方法很像,都是使用了反射的手法,同样的首先通过id 查找到config.xml文件里的name
/frameworks/base/packages/SystemUI/res/values/config.xml
com.android.systemui.statusbar.phone.StatusBar
从这个string name可以发现最后启动的是StatusBar,也就是调用StatusBar的start()方法。
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
public class StatusBar extends SystemUI{
@Override
public void start() {
...
//第一步
createAndAddWindows();
...
}
public void createAndAddWindows() {
//第二步
addStatusBarWindow();
}
...
private void addStatusBarWindow() {
//第三步
makeStatusBarView();
mStatusBarWindowManager = Dependency.get(StatusBarWindowManager.class);
...
//第五步
mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight());
}
...
protected void makeStatusBarView() {
final Context context = mContext;
...
//第四步
inflateStatusBarWindow(context);
}
...
//加载Layout,初始化StatusBarWindow
protected void inflateStatusBarWindow(Context context) {
mStatusBarWindow = (StatusBarWindowView) View.inflate(context,
R.layout.super_status_bar, null);
}
}
在StatusBar类中省略了大量代码,只保留了StatusBar加载的主要流程,从Start()方法中调用createAndAddWindows()接着再调用addStatusBarWindow(),紧接着在makeStatusBarView()方法中通过inflate加载layout的方式初始化StatusBarWindow,将super_status_bar.xml里所设计的样式加载到StatusBar界面上。
也就是说,当我们碰到需要定制化SystemUI的情况下,可以自己自定义一个layout.xml,然后在这里替换掉源文件。到了第5步是在初始化StatusBarWindow后,通过StatusBarWindowManager的add()方法,将statusBarView加载到系统界面上,并设置了statusBar的高度。那么我们就进入StatusBarWindowManager类的add()方法,如下所示:
public class StatusBarWindowManager{
…
/**
* Adds the status bar view to the window manager.
*
* @param statusBarView The view to add.
* @param barHeight The height of the status bar in collapsed state.
*/
public void add(View statusBarView, int barHeight) {
// Now that the status bar window encompasses the sliding panel and its
// translucent backdrop, the entire thing is made TRANSLUCENT and is
// hardware-accelerated.
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.gravity = Gravity.TOP;
mLp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
mLp.setTitle("StatusBar");
mLp.packageName = mContext.getPackageName();
mLp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
mStatusBarView = statusBarView;
mBarHeight = barHeight;
mWindowManager.addView(mStatusBarView, mLp);
mLpChanged = new WindowManager.LayoutParams();
mLpChanged.copyFrom(mLp);
}
}
可以发现StatusBar的加载真正在于WindowManager的处理,先设置好WindowManager.LayoutParams的宽高,层级TYPE,Flag等参数,然后将设置好的LayoutParams和上面传进来的mStatusBarView作为参数,调用addView()方法使View加载到相应的位置上。那么我们反过来思考下,如果将mStatusBarView换成我们自定义的View,那么结果会是什么样?
至此,StatusBar在SystemUI上的加载也就结束了,同样的道理,VolumeBar,NavigationBar等SystemUI其他部分的加载也和StatusBar的加载基本一致,这里就不再做分析。理清了上述代码,再回到文章开头看那张时序图,就可以清楚的知道SystemUI的启动流程,在此基础上,对SystemUI的定制化任务也就变得明朗起来。