SystemUI启动流程分析

转载:https://www.jianshu.com/p/2e0f403e5299

Android 系统 SystemUI 介绍

Android 的 SystemUI 其实就是 Android 的系统界面,它包括了界面上方的状态栏 status bar,下方的导航栏Navigation Bar,锁屏界面 Keyguard ,电源界面 PowerUI,近期任务界面 Recent Task 等等。对于用户而言,SystemUI 的改动是最能直观感受到的。因此,每个 Android 版本在 SystemUI 上都有比较大的改动。而对开发者而言,理解 Android SystemUI 对优化Android系统界面,改善用户体验十分重要。

SystemUI 在哪

在 Andorid 系统源码中,package/apps下放的是系统内置的一些 app,例如 settings,camera,Phone,Message 等等。而在 Framework/base/package 下,它们也是系统的 app,SystemUI 就在此目录下。它控制着整个Android系统的界面,但其实他也是一个 app,不同于一般的 app,它不可卸载也不可以被第三方应用替换。

SystemUI 整体结构

在这里插入图片描述

这是 SystemUI 相关类的继承关系图,可以看到 SystemUI 为基类,每个子类实现了不同的系统界面。

Status Bar 系统上方的状态栏
Navigator Bar 系统下方的导航栏
Keyguard 锁屏界面
PowerUI 电源界面
Recents Screen 近期任务界面
VolumeUI 音量调节对话框
Stack Divider 分屏功能调节器
PipUI 画中画界面
Screenshot 截屏界面
RingtonePlayer 铃声播放器界面
Settings Activity 系统设置中用到的一些界面,例如:NetworkOverLimitActivity,UsbDebuggingActivity等。

在这里插入图片描述

SystemUI的启动流程

先找到 framework/base/service/java/com/android/server/SystemServer.java 文件,里面有个main()方法,main 方法如下:

public static void main(String[] args){
    new SystemServer().run()
}

main 方法里启动了 run() 方法,而在 run 方法中调用了 startBootstrapServices() 方法和 startOtherServices() 方法,在 startOtherServices() 里 mActivityManagerService.systemReady 创建线程去执行startSystemUi(context),这里将启动 SystemUI。具体方法如下:

mActivityManagerService.systemReady(new Runnable() {
            @Override
            public void run() {
                ......
                Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "StartSystemUI");
                try {
                    startSystemUi(context);
                } catch (Throwable e) {
                    reportWtf("starting System UI", e);
                }
                Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
                ......
            }
        });
    static final void startSystemUi(Context context) {
        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);
    }

然后我们进入设置启动 systemui 程序的 SystemUIService 文件里,该文件在framework/base/packages/SystemUI/src/com/android/systemui/SystemUIService.java.我们看该文件的onCreate() 方法。方法如下:

    @Override
    public void onCreate() {
        super.onCreate();
        ((SystemUIApplication) getApplication()).startServicesIfNeeded();
    }

可以看到有一句 ((SystemUIApplication) getApplication()).startServicesIfNeeded(),这句很关键,我们再进入 startServicesIfNeeded(),看看具体是如何启动系统服务的。该方法如下:

    public void startServicesIfNeeded() {
        startServicesIfNeeded(SERVICES);
    }
    
    /**
     * The classes of the stuff to start.
     */
    private final Class<?>[] SERVICES = new Class[] {
    		com.android.systemui.tuner.TunerService.class, //定制状态栏服务
            com.android.systemui.keyguard.KeyguardViewMediator.class,//锁屏模块
            com.android.systemui.recents.Recents.class,//最近应用
            com.android.systemui.volume.VolumeUI.class,//全局音量控制
            com.android.systemui.statusbar.SystemBars.class,//系统状态栏
            com.android.systemui.usb.StorageNotification.class,//Storage存储通知
            com.android.systemui.power.PowerUI.class,//电量管理相关
            com.android.systemui.media.RingtonePlayer.class,//铃声播放
            com.android.systemui.keyboard.KeyboardUI.class,//键盘相关
            Divider.class,  //分屏功能调节器
            com.android.systemui.tv.pip.PipUI.class, //画中画界面
            com.android.systemui.shortcut.ShortcutKeyDispatcher.class,  //截屏界面
            com.android.systemui.VendorServices.class
    };
   
private void startServicesIfNeeded(Class<?>[] services) {
        //已经启动了,则不再启动
        if (mServicesStarted) {
            return;
        }

        if (!mBootCompleted) {
            // check to see if maybe it was already completed long before we began
            // see ActivityManagerService.finishBooting()
            // 看是否ActivityManagerService完成启动
            if ("1".equals(SystemProperties.get("sys.boot_completed"))) {
                mBootCompleted = true;
                if (DEBUG) Log.v(TAG, "BOOT_COMPLETED was already sent");
            }
        }

        Log.v(TAG, "Starting SystemUI services for user " +
                Process.myUserHandle().getIdentifier() + ".");
        final int N = services.length;
        for (int i=0; i<N; i++) {
            Class<?> cl = services[i];
            if (DEBUG) Log.d(TAG, "loading: " + cl);
            try {
                //SystemUIFactory未实现,==null
                Object newService = SystemUIFactory.getInstance().createInstance(cl);
                mServices[i] = (SystemUI) ((newService == null) ? cl.newInstance() : newService);
            } 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();

            if (mBootCompleted) {
               //如果ActivitymanagerService启动完成,调用个SystemUI的onBootCompleted
                mServices[i].onBootCompleted();
            }
        }
        mServicesStarted = true;
    }

看到这里我们就明白了,这里是拿到每个和 SystemUI 相关的类的反射,存到了 service[] 里,然后赋值给cl,紧接着将通过反射将其转化为具体类的对象,存到了mService[i]数组里,最后对象调 start() 方法启动相关类的服务,启动完成后,回调 onBootCompleted( ) 方法。
mService[i] 里的值不同时,调用的 start() 方法也不相同,这里我们以S ystemBars 的 start() 为例,所以mService[i].start() 先认为是 SystemBars.start().

SystemBars

SystemBars.java 位于framework/base/packages/SystemUI/res/com/android/systemui/statusbar/SystemBars.java ,找到 start() 方法:

    @Override
    public void start() {
        if (DEBUG) Log.d(TAG, "start");
        mServiceMonitor = new ServiceMonitor(TAG, DEBUG,
                mContext, Settings.Secure.BAR_SERVICE_COMPONENT, this);
        mServiceMonitor.start();  // will call onNoService if no remote service is found
    }
 ServiceMonitor的start()方法   
 public void start() {
        // listen for setting changes
        ContentResolver cr = mContext.getContentResolver();
        cr.registerContentObserver(Settings.Secure.getUriFor(mSettingKey),
                false /*notifyForDescendents*/, mSettingObserver, UserHandle.USER_ALL);

        // listen for package/component changes
        IntentFilter filter = new IntentFilter();
        filter.addAction(Intent.ACTION_PACKAGE_ADDED);
        filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
        filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
        filter.addDataScheme("package");
        mContext.registerReceiver(mBroadcastReceiver, filter);

        mHandler.sendEmptyMessage(MSG_START_SERVICE);
    }

最后一句,开启服务:
    private void startService() {
        mServiceName = getComponentNameFromSetting();
        if (mDebug) Log.d(mTag, "startService mServiceName=" + mServiceName);
        if (mServiceName == null) {
            mBound = false;
            mCallbacks.onNoService();
        } else {
            long delay = mCallbacks.onServiceStartAttempt();
            mHandler.sendEmptyMessageDelayed(MSG_CONTINUE_START_SERVICE, delay);
        }
    }
没有服务,执行Systembar的onNoService()

onNoService()方法,进入 onNoService() 方法,该方法就位于 start() 方法下方,可以看到方法中调用了CreateStatusBarFromConfig() 该方法如下:

 private void createStatusBarFromConfig() {
        if (DEBUG) Log.d(TAG, "createStatusBarFromConfig");
        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;
        try {
            cls = mContext.getClassLoader().loadClass(clsName);
        } catch (Throwable t) {
            throw andLog("Error loading status bar component: " + clsName, t);
        }
        try {
            mStatusBar = (BaseStatusBar) 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());
    }

    <string name="config_statusBarComponent" translatable="false">com.android.systemui.statusbar.phone.PhoneStatusBar</string>

该方法中先读取 value/config.xml 文件中 config_statusBarComponent 的值,这里为:com.android.systemui.statusbar.phone.PhoneStatusBar,然后通过反射得到了 PhoneStatusBar 对象,最后的 mStartus.start() 也就等于 PhoneStatusBar.start(),进入该方法,会发现,里面调用了super.start(),也就是先执行了父类的 start() ,其父类为 BaseStatusBar,该类的star()方法较多,就不放出来了,我们看重点,找到里面有调用一个 createAndAddWindows(),该方法为抽象方法,则会调用它的子类的方法,这里也就是 PhoneStatusBar 的 createAndAddWindows()方法,如下:

    @Override
    public void createAndAddWindows() {
        addStatusBarWindow();
    }

    private void addStatusBarWindow() {
        makeStatusBarView();
        mStatusBarWindowManager = new StatusBarWindowManager(mContext);
        mRemoteInputController = new RemoteInputController(mStatusBarWindowManager,
                mHeadsUpManager);
        mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight());
    }

createAndAddWindows() 里只调用了 addStaBarWindow() 方法,而在该方法里,调用了makeStartusBarView,看名字就知道该方法关键,意为构建statusBar视图。该方法很长,里面有inflateStatusBarWindow(),进入该方法,可以看到,这么一句:

    protected void inflateStatusBarWindow(Context context) {
        mStatusBarWindow = (StatusBarWindowView) View.inflate(context,
                R.layout.super_status_bar, null);
    }

然后,我们通过 super_status_bar.xml 的分析 SystemBars 的大致视图构成了,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">

    <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.statusbar.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.statusbar.AlphaOptimizedView
        android:id="@+id/heads_up_scrim"
        android:layout_width="match_parent"
        android:layout_height="@dimen/heads_up_scrim_height"
        android:background="@drawable/heads_up_scrim"
        sysui:ignoreRightInset="true"
        android:importantForAccessibility="no"/>

    <com.android.systemui.statusbar.VisualizerView
        android:id="@+id/visualizerview"
        android:gravity="bottom"
        android:layout_gravity="bottom"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:visibility="visible"
        sysui:ignoreRightInset="true" />

    <include layout="@layout/status_bar"
        android:layout_width="match_parent"
        android:layout_height="@dimen/status_bar_height" />

    <include layout="@layout/brightness_mirror" />

    <ViewStub android:id="@+id/fullscreen_user_switcher_stub"
              android:layout="@layout/car_fullscreen_user_switcher"
              android:layout_width="match_parent"
              android:layout_height="match_parent"/>

    <include layout="@layout/status_bar_expanded"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:visibility="gone" />

    <com.android.systemui.statusbar.ScrimView android:id="@+id/scrim_in_front"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:importantForAccessibility="no"
        sysui:ignoreRightInset="true"
        />

</com.android.systemui.statusbar.phone.StatusBarWindowView>

super_status_bar.xml
super_status_bar.xml 中 include 了一个名称为 status_bar 的布局文件
super_status_bar.xml*中 include 了一个名称为 status_bar_expanded 的布局文件
这里的 status_bar 便是系统状态栏的布局文件,status_bar_expanded 便是下拉的通知窗口的布局文件
上述 super_status_bar.xml 与如下视图对应:
在这里插入图片描述

PhoneStatusBarView

即为手机最上方的状态栏,主要用于显示系统状态,通知等,主要包括 notification icons 和 status bar icons。

PanelHolder

PanelHolder是用户下拉 status bar 后得到的 view。它主要包含 QuickSettings 和 Notification panel 两个部分。
PanelHolder是一个继承自 FrameLayout的自定义 view,它的内容是通过 include status_bar_expanded.xml进行填充的。
PanelHolder的布局比较复杂,为了提高 view 的重用性大量的使用了 include 标签。

status_bar_expanded.xml 对应的视图:
在这里插入图片描述

KeyguardBouncer

KeyguardBouncer是锁屏解锁界面,根据用户设置的解锁方式不同,展示不同的解锁模式。
keyguard_bouncer.xml 对应的 KerguardBouncer 视图:

在这里插入图片描述

附:
SystemUI启动流程图:

在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值