Android P下SystemUI的启动与定制化

众所周知SystemUI包含基本的StatusBar、VolumeBar、NavigationBar等部分,在手机开机时就已经为咱们加载好,可是有时候会出现对StatusBar,DropList等进行定制化的任务,那么就须要了解SystemUI的启动流程,了解StatusBar,DropList等view是如何加载在系统界面上,下文是从SystemUI启动入口、SystemUI的加载机制以及以StatusBar为例来分析整个流程。下图为SystemUI启动的整个时序图: java

1、SystemUI的启动入口

SystemUI的加载是在Android系统启动的时候,那么咱们能够知道SystemUI的入口多是在系统启动的流程中。经过调查,发如今SyetemServer进程中开始启动系统服务,如AMS,PMS,蓝牙,窗口管理服务等,其中就包括SystemUI,那么就来看看代码中是如何实现的。android

/frameworks/base/services/java/com/android/server/SystemServer.javabootstrap

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.
        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.
        createSystemContext();

        // Create the system service manager.
        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();
            startCoreServices();
            startOtherServices();
            SystemServerInitThreadPool.shutdown();
        } catch (Throwable ex) {
            Slog.e("System", "******************************************");
            Slog.e("System", "************ Failure starting system services", ex);
            throw ex;
        } finally {
            traceEnd();
        }
        
        ...
    }

复制代码

在SystemServer中能够发现启动SystemServer的是zygote进程,这个不属于本文范畴先不作探讨。在SystemServer的Main函数中,调用了run(),那么跟进到run方法中(上述代码省略了一部分,只保留主线),首先初始化了native services和system context,接着建立一个SystemServiceManager对象用于后续系统服务的启动和管理。初始化完成,接下来就开始系统服务的启动,这里调用了startBootstrapServices()、startCoreServices()、startOtherServices()三个方法,从名字来看,分别是启动引导service、启动中心service、启动其余的services,这三个方法就是开启不一样的系统服务的入口,那就分别进入到三个方法中。数组

  • 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等系统所需的一些小的关键服务;bash

  • 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()来开启服务,只不过里面的参数不一样,就不作详细的探讨,继续看第三个方法;app

  • startOtherServices()
private void startOtherServices() {
        ...
        traceBeginAndSlog("StartSystemUI");
            try {
                startSystemUi(context, windowManagerF);
            } catch (Throwable e) {
                reportWtf("starting System UI", e);
            }
    
    }
            
复制代码

在第三个startOtherServices()方法中,除掉开启一些系统所需的服务外,最主要的核心在于startSystemUi()方法,里面传入systemContext和WindowManagerService两个参数,也就是说咱们已经找到systemUI启动的入口,那么就继续进入到startSystemUi()方法中。ide

  • 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中查看详细的启动流程。函数

2、SystemUI开始加载

第一部分找到了SystemUIService的启动,那么就先进入到SystemUIService类中。以下所示: /frameworks/base/packages/SystemUI/src/com/android/systemui/SystemUIService.javaui

public class SystemUIService extends Service {

    @Override
    public void onCreate() {
        super.onCreate();
        ((SystemUIApplication) getApplication()).startServicesIfNeeded();
    ...
    }
}
复制代码

在onCreate()中获取了SystemUIApplication而且调用了它的startServicesIfNeeded()方法,那么接着就进入到SystemUIApplication类中,在SystemUIApplication类中找到startServicesIfNeeded方法,以下。this

  • startServicesIfNeeded()
public class SystemUIApplication extends Application implements SysUiServiceProvider {
    ...
    
    public void startServicesIfNeeded() {
        String[] names = getResources().getStringArray(R.array.config_systemUIServiceComponents);
        startServicesIfNeeded(names);
    }
    
    ...
}
复制代码

startServicesIfNeeded()方法中,首先建立了一个包含services的名字的数组,接着将获取的数组做为参数调用startServicesIfNeeded(String[] services)方法,这里先不看这个方法内部的构造,先来看看上面数组获取的都有哪些services,根据代码中提供的id R.array.config_systemUIServiceComponents咱们在xml中找到config.xml这个文件,其中发现了一些数据,以下: /frameworks/base/packages/SystemUI/res/values/config.xml

<string-array name="config_systemUIServiceComponents" translatable="false">
        <item>com.android.systemui.Dependency</item>
        <item>com.android.systemui.util.NotificationChannels</item>
        <item>com.android.systemui.statusbar.CommandQueue$CommandQueueStart</item>
        <item>com.android.systemui.keyguard.KeyguardViewMediator</item>
        <item>com.android.systemui.recents.Recents</item>
        <item>com.android.systemui.volume.VolumeUI</item>
        <item>com.android.systemui.stackdivider.Divider</item>
        <item>com.android.systemui.SystemBars</item>
        <item>com.android.systemui.usb.StorageNotification</item>
        <item>com.android.systemui.power.PowerUI</item>
        <item>com.android.systemui.media.RingtonePlayer</item>
        <item>com.android.systemui.keyboard.KeyboardUI</item>
        <item>com.android.systemui.pip.PipUI</item>
        <item>com.android.systemui.shortcut.ShortcutKeyDispatcher</item>
        <item>@string/config_systemUIVendorServiceComponent</item>
        <item>com.android.systemui.util.leak.GarbageMonitor$Service</item>
        <item>com.android.systemui.LatencyTester</item>
        <item>com.android.systemui.globalactions.GlobalActionsComponent</item>
        <item>com.android.systemui.ScreenDecorations</item>
        <item>com.android.systemui.fingerprint.FingerprintDialogImpl</item>
        <item>com.android.systemui.SliceBroadcastRelayHandler</item>
    </string-array>
复制代码

在上面这些item中,能够发现都是一些咱们所熟悉的类,例如VolumeUI、SystemBars、PowerUI、KeyboardUI等,也就是咱们手机界面常看见的系统音量,锁屏,状态栏等,而这些UI正是SystemUI的构造部分。

在startServicesIfNeeded()方法中先将这些小部件集合在一块儿,而后调用startServicesIfNeeded(String[] services),那么咱们能够猜想接下来是否是就要开始分别加载这些小部件而且将他们放置在相应的位置上。

  • 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放置在不一样的位置上的,咱们继续往下看。

3、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

<string name="config_statusBarComponent" translatable="false">com.android.systemui.statusbar.phone.StatusBar</string>
复制代码

从这个string name能够发现最后启动的是StatusBar,也就是调用StatusBar的start()方法。

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的定制化任务也就变得明朗起来。

参考资料:

Android系统启动流程(三)解析SyetemServer进程启动过程

相关文章
相关标签/搜索