SystemUI之StatusBar建立

本文是基于Android 10代码分析。java

启动 SystemUI服务

ActivityManagerService 建立完成时会启动 SystemUIandroid

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

    private void startOtherServices() {
        mActivityManagerService.systemReady(() -> {
            try {
                // AMS启动完成时,开启SystemUI
                // 其实就是start一个叫SystemUIService的service
                startSystemUi(context, windowManagerF);
            } catch (Throwable e) {
                    reportWtf("starting System UI", e);
            }
        }
    }
复制代码

启动 SystemUI 的入口是 SystemUIService,它是四大组件之一的 Service。数组

// frameworks/base/packages/SystemUI/src/com/android/systemui/SystemUIService.java
    
    public class SystemUIService extends Service {
        @Override
        public void onCreate() {
            super.onCreate();
            // 启动各类服务
            ((SystemUIApplication) getApplication()).startServicesIfNeeded();
        }
    }
复制代码

调用了 SystemUIApplication#startServicesIfNeeded() 来启动各类服务,而这些服务不是四大组件之一的 Service, 而是继承自 SystemUI 接口的服务,咱们称之为 SystemUI服务。app

// frameworks/base/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
    
    public void startServicesIfNeeded() {
        // 这个数组中定义了不少SystemUI服务
        String[] names = getResources().getStringArray(R.array.config_systemUIServiceComponents);
        // 启动服务
        startServicesIfNeeded(names);
    }
复制代码

SystemUI 要启动的全部服务都是在数组 config_systemUIServiceComponents 中定义的,能够看下这个数组的定义ide

// frameworks/base/packages/SystemUI/res/values/config.xml
    
    <string-array name="config_systemUIServiceComponents" translatable="false">
        <item>com.android.systemui.Dependency$DependencyCreator</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.biometrics.BiometricDialogImpl</item>
        <item>com.android.systemui.SliceBroadcastRelayHandler</item>
        <item>com.android.systemui.SizeCompatModeActivityController</item>
        <item>com.android.systemui.statusbar.notification.InstantAppNotifier</item>
        <item>com.android.systemui.theme.ThemeOverlayController</item>
    </string-array>    
复制代码

对这些 SystemUI服务 作一点介绍布局

  1. com.android.systemui.Dependency 是为了建立全局可用的依赖关系。
  2. com.android.systemui.SystemBars 建立整个SystemUI视图的入口类。
  3. com.android.systemui.statusbar.CommandQueue 是一个 Binder 类,它会被StatusBar注册到 StatusBarManagerService 中,用于接收StatusBarManagerService服务端的消息。

如今接着上面,来看下如何启动这些 SystemUI服务ui

private void startServicesIfNeeded(String[] services) {
        if (mServicesStarted) {
            return;
        }
        mServices = new SystemUI[services.length];

        if (!mBootCompleted) {
            // check to see if maybe it was already completed long before we began
            // see ActivityManagerService.finishBooting()
            // sys.boot_completed 属性能够判断系统是否已经启动完毕
            if ("1".equals(SystemProperties.get("sys.boot_completed"))) {
                mBootCompleted = true;
            }
        }

        final int N = services.length;
        for (int i = 0; i < N; i++) {
            String clsName = services[i];
            Class cls;
            try {
                // 1. 经过反射建立实例
                cls = Class.forName(clsName);
                Object o = cls.newInstance();
                if (o instanceof SystemUI.Injector) {
                    o = ((SystemUI.Injector) o).apply(this);
                }
                // 强转为SystemUI保存
                mServices[i] = (SystemUI) o;
            } catch(Exception ex){
                throw new RuntimeException(ex);
            }
            
            mServices[i].mContext = this;
            // 注意这里把mComponents也传进去了,用于全局保存变量,Class<?> -> Object
            mServices[i].mComponents = mComponents;
            
            // 2. 调用start()启动
            mServices[i].start();

            // 3. 系统启动完毕,还用启动相应的onBootCompleted()
            if (mBootCompleted) {
                mServices[i].onBootCompleted();
            }
        }
       
        mServicesStarted = true;
    }
复制代码

首先根据 SystemUI服务的类名,经过反制加载类而且建立类的对象,而后调用对象的 start() 方法和 onBootCompleted() 方法。this

建立StatusBar

如今把目光集中到如何建立 StatusBar 上。前面说过,SystemBars 这个 SystemUI服务是整个System视图建立的入口类,它被启动时会调用 start() 方法spa

// frameworks/base/packages/SystemUI/src/com/android/systemui/SystemBars.java
    
    public void start() {
        // 也就是调用 StatusBar#start()
        createStatusBarFromConfig();
    }
复制代码

createStatusBarFromConfig()很简单,就是调用了StatusBar类的start()方法。code

StatusBar#start() 代码很庞大,本文主要分析状态栏的建立,至于状态栏上图标的添加留到下一篇文章进行分析。

// packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
    public void start() {
        // 建立整个SystemUI视图并添加到WindowManager中
        createAndAddWindows(result);
    }
    
    public void createAndAddWindows(@Nullable RegisterStatusBarResult result) {
        // 建立整个SystemUI视图
        makeStatusBarView(result);
        // 把视图添加到Window中
        mStatusBarWindowController = Dependency.get(StatusBarWindowController.class);
        mStatusBarWindowController.add(mStatusBarWindow, getStatusBarHeight());
    }    
复制代码

makeStatusBarView()负责建立整个SystemUI视图,其中包括状态栏。下面的代码主要提取了建立视图的代码,分几步进行分析

// packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
    
    protected void makeStatusBarView(@Nullable RegisterStatusBarResult result) {
        // ...
        
        // 1. 实例化整个SystemUI视图,包括状态栏,通知面版, 锁屏
        mStatusBarWindow = (StatusBarWindowView) mInjectionInflater.injectable(
                LayoutInflater.from(context)).inflate(R.layout.super_status_bar, null);    
    }
复制代码

首先建立整个SystemUI视图,它的布局是 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">

    <!--这里省略了可有可无的视图-->

    <!--状态栏容器-->
    <FrameLayout android:id="@+id/status_bar_container" android:layout_width="match_parent" android:layout_height="wrap_content" />

    <!--整个下拉通知面版,包括锁屏界面-->
    <include layout="@layout/status_bar_expanded" android:layout_width="match_parent" android:layout_height="match_parent" android:visibility="invisible" />

    <!--这里省略了可有可无的视图-->

    <!--这个显示在锁屏底部区域-->
    <LinearLayout android:id="@+id/lock_icon_container" android:orientation="vertical" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="@dimen/status_bar_height" android:layout_gravity="top|center_horizontal">
        <com.android.systemui.statusbar.phone.LockIcon android:id="@+id/lock_icon" android:layout_width="@dimen/keyguard_lock_width" android:layout_height="@dimen/keyguard_lock_height" android:layout_gravity="center_horizontal" android:layout_marginTop="@dimen/keyguard_lock_padding" android:contentDescription="@string/accessibility_unlock_button" android:src="@*android:drawable/ic_lock" android:scaleType="center" />
        <com.android.keyguard.KeyguardMessageArea android:id="@+id/keyguard_message_area" style="@style/Keyguard.TextView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="@dimen/keyguard_lock_padding" android:gravity="center" android:singleLine="true" android:ellipsize="marquee" android:focusable="true" />
    </LinearLayout>
</com.android.systemui.statusbar.phone.StatusBarWindowView>
复制代码

根视图StatusBarWindowView是一个FrameLayout,那么状态栏显示在最下面,而后通知面版会覆盖状态栏,最后还有一个底部视图在最上面。

能够注意到状态栏的窗口ID为status_bar_container,一会就会向这个容器中添加状态栏视图。如今接着上面的makeStatusBarView()继续分析

// packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
    
    protected void makeStatusBarView(@Nullable RegisterStatusBarResult result) {
        // ...
        
        // 1. 实例化整个SystemUI视图,包括状态栏,通知面版, 锁屏
        mStatusBarWindow = (StatusBarWindowView) mInjectionInflater.injectable(
                LayoutInflater.from(context)).inflate(R.layout.super_status_bar, null);   
        
        // 2.建立状态栏视图 
        FragmentHostManager.get(mStatusBarWindow)
                .addTagListener(CollapsedStatusBarFragment.TAG, (tag, fragment) -> {
                    CollapsedStatusBarFragment statusBarFragment =
                            (CollapsedStatusBarFragment) fragment;
                    // 用通知图标控制器,初始化了通知图标区域和中心图标区域,而且显示出来
                    statusBarFragment.initNotificationIconArea(mNotificationIconAreaController);
                }).getFragmentManager()
                .beginTransaction()
                // CollapsedStatusBarFragment实现了状态栏的添加
                .replace(R.id.status_bar_container, new CollapsedStatusBarFragment(),
                        CollapsedStatusBarFragment.TAG)
                .commit();                
    }
复制代码

能够看到CollapsedStatusBarFragment表明的就是状态栏视图,这个视图被添加到ID为status_bar_container的容器中。接下来只要分析CollapsedStatusBarFragment的生命周期,便可知道状态栏的建立过程,首先看的就是CollapsedStatusBarFragment#onCreateView()方法,这里就是建立视图的地方

// frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
    
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
        // status bar视图
        return inflater.inflate(R.layout.status_bar, container, false);
    }
复制代码

status_bar.xml 就是状态栏视图布局,接下来再简单看下布局状况

<com.android.systemui.statusbar.phone.PhoneStatusBarView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui" android:layout_width="match_parent" android:layout_height="@dimen/status_bar_height" android:id="@+id/status_bar" android:background="@drawable/system_bar_background" android:orientation="vertical" android:focusable="false" android:descendantFocusability="afterDescendants" android:accessibilityPaneTitle="@string/status_bar" >

    <!--这里省略了可有可无的视图-->

    <LinearLayout android:id="@+id/status_bar_contents" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingStart="@dimen/status_bar_padding_start" android:paddingEnd="@dimen/status_bar_padding_end" android:paddingTop="@dimen/status_bar_padding_top" android:orientation="horizontal" >
        <FrameLayout android:layout_height="match_parent" android:layout_width="0dp" android:layout_weight="1">

            <!--这个好像是在状态栏显示信息布局-->
            <include layout="@layout/heads_up_status_bar_layout" />

            <!--状态栏左边视图,依次显示运营商名字,时间,通知图标-->
            <LinearLayout android:id="@+id/status_bar_left_side" android:layout_height="match_parent" android:layout_width="match_parent" android:clipChildren="false" >
                <ViewStub android:id="@+id/operator_name" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout="@layout/operator_name" />

                <com.android.systemui.statusbar.policy.Clock android:id="@+id/clock" android:layout_width="wrap_content" android:layout_height="match_parent" android:textAppearance="@style/TextAppearance.StatusBar.Clock" android:singleLine="true" android:paddingStart="@dimen/status_bar_left_clock_starting_padding" android:paddingEnd="@dimen/status_bar_left_clock_end_padding" android:gravity="center_vertical|start" />

                <!--通知图标区域-->
                <com.android.systemui.statusbar.AlphaOptimizedFrameLayout android:id="@+id/notification_icon_area" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:orientation="horizontal" android:clipChildren="false"/>

            </LinearLayout>
        </FrameLayout>

        <!--这里省略了可有可无的视图-->
        
        <!--中心图标区域-->
        <com.android.systemui.statusbar.AlphaOptimizedFrameLayout android:id="@+id/centered_icon_area" android:layout_width="wrap_content" android:layout_height="match_parent" android:orientation="horizontal" android:clipChildren="false" android:gravity="center_horizontal|center_vertical"/>

        <!--系统图标区,由状态图标集和电池图标组成,状态图标集由wifi,bt等等组成-->
        <com.android.keyguard.AlphaOptimizedLinearLayout android:id="@+id/system_icon_area" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:orientation="horizontal" android:gravity="center_vertical|end" >
            
            <include layout="@layout/system_icons" />
        </com.android.keyguard.AlphaOptimizedLinearLayout>
    </LinearLayout>

    <!--这里省略了可有可无的视图-->

</com.android.systemui.statusbar.phone.PhoneStatusBarView>
复制代码

从这个布局能够分析出状态栏从左到右到底显示了什么。

  1. 最左边的一块区域,依次显示运营商名字,时间,通知图标。
  2. 中间一块区域,这个我暂时还没发现显示什么。
  3. 而后是最右边一块区域,显示的是状态图标(例如bt, wifi),以及电池图标。

这样一来,咱们就对整个状态栏上的布局有个了解,接下来分析下状态上的状态图标(例如bt, wifi)是如何显示上去的,这就是后面一篇文章分析的主要内容。

相关文章
相关标签/搜索