Android 13.0 StatusBar顶部图标加载流程

简介: Android 13.0 StatusBar顶部图标加载流程

学习笔记:

说到 StatusBar 的顶部图标,就不得不提起以下两个类:

  • PhoneStatusBarPolicy:定义了系统通知图标的设置策略;
  • StatusBarSignalPolicy:定义了状态栏网络信号策略;

StatusBarSignalPolicy 前面有过分析,这次以 PhoneStatusBarPolicy 为例进行分析;

PhoneStatusBarPolicy 在 StatusBar 的 start() 方法里初始化:

// StatusBar.java 
@Override
public void start() {
    // 省略部分代码......
    mStatusBarSignalPolicy.init();  // 这里Android 13 与前面 Android 11 的初始化方法有点不同。
    // 创建整个SystemUI视图并添加到WindowManager中
    createAndAddWindows();//这个重点方法,创建相关的视图
    // 省略部分代码......
    // 调用图标策略来显示更新所有图标。
    mIconPolicy.init();
    // 省略部分代码......
}

这里直接看PhoneStatusBarPolicy#init() 这·个·方法很关键:

// PhoneStatusBarPolicy.java
 public void init() {
        // 注册广播监听。
        IntentFilter filter = new IntentFilter();
        // 耳机是否插入
        filter.addAction(AudioManager.ACTION_HEADSET_PLUG);
        // SIM卡广播
        filter.addAction(Intent.ACTION_SIM_STATE_CHANGED);
        // TTY 模式已更改,(文字电话)
        filter.addAction(TelecomManager.ACTION_CURRENT_TTY_MODE_CHANGED);
        // 可用 配置文件
        filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
        // 不可用 配置文件
        filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
        // 删除 配置文件
        filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
        /// 添加用户切换操作以更新可能的警报图标
        filter.addAction(Intent.ACTION_USER_SWITCHED);
        // 向处理程序注册接收器
        mBroadcastDispatcher.registerReceiverWithHandler(mIntentReceiver, filter, mHandler,UserHandle.ALL);
        // 注册闹钟已更改
        registerAlarmClockChanged(UserHandle.USER_OWNER, false);
        Observer<Integer> observer = ringer -> mHandler.post(this::updateVolumeZen);
        mRingerModeTracker.getRingerMode().observeForever(observer);
        mRingerModeTracker.getRingerModeInternal().observeForever(observer);
        // 监听用户配置文件更改.
        try {
            mIActivityManager.registerUserSwitchObserver(mUserSwitchListener, TAG);
        } catch (RemoteException e) {
            // Ignore
        }
        // TTY状态
        updateTTY();
        // 蓝牙状态
        updateBluetooth();
        //eMBMS状态
        mIconController.setIcon(mSlotEmbms, R.drawable.stat_sys_embms, null);
        mIconController.setIconVisibility(mSlotEmbms, false);
        // 闹钟
        mIconController.setIcon(mSlotAlarmClock, R.drawable.stat_sys_alarm, null);
        mIconController.setIconVisibility(mSlotAlarmClock, false);
        // zen
        mIconController.setIcon(mSlotZen, R.drawable.stat_sys_dnd, null);
        mIconController.setIconVisibility(mSlotZen, false);
        // 振动
        mIconController.setIcon(mSlotVibrate, R.drawable.stat_sys_ringer_vibrate,
                mResources.getString(R.string.accessibility_ringer_vibrate));
        mIconController.setIconVisibility(mSlotVibrate, false);
        // mute
        mIconController.setIcon(mSlotMute, R.drawable.stat_sys_ringer_silent,
                mResources.getString(R.string.accessibility_ringer_silent));
        mIconController.setIconVisibility(mSlotMute, false);
        updateVolumeZen();
        // cast
        mIconController.setIcon(mSlotCast, R.drawable.stat_sys_cast, null);
        mIconController.setIconVisibility(mSlotCast, false);
        // 热点
        mIconController.setIcon(mSlotHotspot, R.drawable.stat_sys_hotspot,
                mResources.getString(R.string.accessibility_status_bar_hotspot));
        mIconController.setIconVisibility(mSlotHotspot, mHotspot.isHotspotEnabled());
        // managed profile
        updateManagedProfile();
        // 数据保护程序
        mIconController.setIcon(mSlotDataSaver, R.drawable.stat_sys_data_saver,
                mResources.getString(R.string.accessibility_data_saver_on));
        mIconController.setIconVisibility(mSlotDataSaver, false);
        // 隐私 items
        String microphoneString = mResources.getString(PrivacyType.TYPE_MICROPHONE.getNameId());
        String microphoneDesc = mResources.getString(
                R.string.ongoing_privacy_chip_content_multiple_apps, microphoneString);
        mIconController.setIcon(mSlotMicrophone, PrivacyType.TYPE_MICROPHONE.getIconId(),
                microphoneDesc);
        mIconController.setIconVisibility(mSlotMicrophone, false);
        String cameraString = mResources.getString(PrivacyType.TYPE_CAMERA.getNameId());
        String cameraDesc = mResources.getString(
                R.string.ongoing_privacy_chip_content_multiple_apps, cameraString);
        mIconController.setIcon(mSlotCamera, PrivacyType.TYPE_CAMERA.getIconId(),
                cameraDesc);
        mIconController.setIconVisibility(mSlotCamera, false);
        mIconController.setIcon(mSlotLocation, LOCATION_STATUS_ICON_ID,
                mResources.getString(R.string.accessibility_location_active));
        mIconController.setIconVisibility(mSlotLocation, false);
        // 传感器关闭
        mIconController.setIcon(mSlotSensorsOff, R.drawable.stat_sys_sensors_off,
                mResources.getString(R.string.accessibility_sensors_off_active));
        mIconController.setIconVisibility(mSlotSensorsOff,
                mSensorPrivacyController.isSensorPrivacyEnabled());
        // 录屏
        mIconController.setIcon(mSlotScreenRecord, R.drawable.stat_sys_screen_record, null);
        mIconController.setIconVisibility(mSlotScreenRecord, false);
        mRotationLockController.addCallback(this);
        mBluetooth.addCallback(this);
        mProvisionedController.addCallback(this);
        mZenController.addCallback(this);
        mCast.addCallback(mCastCallback);
        mHotspot.addCallback(mHotspotCallback);
        mNextAlarmController.addCallback(mNextAlarmCallback);
        mDataSaver.addCallback(this);
        mKeyguardStateController.addCallback(this);
        mPrivacyItemController.addCallback(this);
        mSensorPrivacyController.addCallback(mSensorPrivacyListener);
        mLocationController.addCallback(this);
        mRecordingController.addCallback(this);
        mCommandQueue.addCallback(this);
    }

每个icon对应一个updatexxx(),依靠监听和回调机制,可以实现控制状态栏icon图标的显示、隐藏。

上述就是图标的加载显示和移除了,那么布局又是在哪添加的呢??


下面接着看 CollapsedStatusBarFragment ,启动方式前面讲过,这里不在重复;看其的 onViewCreated():

CollapsedStatusBarFragment #onViewCreated()

// CollapsedStatusBarFragment.java
    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        mStatusBarFragmentComponent = mStatusBarFragmentComponentFactory.create(this);
        mStatusBarFragmentComponent.init();
        mStatusBar = (PhoneStatusBarView) view;
        View contents = mStatusBar.findViewById(R.id.status_bar_contents);
        contents.addOnLayoutChangeListener(mStatusBarLayoutListener);
        updateStatusBarLocation(contents.getLeft(), contents.getRight());
        if (savedInstanceState != null && savedInstanceState.containsKey(EXTRA_PANEL_STATE)) {
            mStatusBar.restoreHierarchyState(
                    savedInstanceState.getSparseParcelableArray(EXTRA_PANEL_STATE));
        }
        mDarkIconManager = new DarkIconManager(view.findViewById(R.id.statusIcons), mFeatureFlags);
        mDarkIconManager.setShouldLog(true);
        updateBlockedIcons();
        mStatusBarIconController.addIconGroup(mDarkIconManager);
        mSystemIconArea = mStatusBar.findViewById(R.id.system_icon_area);
        mClockView = mStatusBar.findViewById(R.id.clock);
        mOngoingCallChip = mStatusBar.findViewById(R.id.ongoing_call_chip);
        showSystemIconArea(false);
        showClock(false);
        initEmergencyCryptkeeperText();
        initOperatorName();
        initNotificationIconArea();
        mSystemEventAnimator =
                new StatusBarSystemEventAnimator(mSystemIconArea, getResources());
        mCarrierConfigTracker.addCallback(mCarrierConfigCallback);
        mCarrierConfigTracker.addDefaultDataSubscriptionChangedListener(mDefaultDataListener);
    }

补充:在 status_bar.xml 中

  • system_icon_area:就是显示蓝牙、wifi、VPN、网卡icon那块区域。

接着我看可以注意到这么几句代码:

mDarkIconManager = new DarkIconManager(view.findViewById(R.id.statusIcons), mFeatureFlags);
        mDarkIconManager.setShouldLog(true);
        updateBlockedIcons();
        // 添加图标组
        mStatusBarIconController.addIconGroup(mDarkIconManager);

上述 R.id.statusIcons 即是 system_icons.xml 里面的控件。

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              xmlns:systemui="http://schemas.android.com/apk/res-auto"
    android:id="@+id/system_icons"
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    android:layout_gravity="center_vertical|end"
    android:gravity="center_vertical">
    <com.android.systemui.statusbar.phone.StatusIconContainer android:id="@+id/statusIcons"
        android:layout_width="0dp"
        android:layout_weight="1"
        android:layout_height="match_parent"
        android:paddingEnd="@dimen/signal_cluster_battery_padding"
        android:gravity="center_vertical"
        android:orientation="horizontal"/>
    <com.android.systemui.battery.BatteryMeterView android:id="@+id/battery"
        android:layout_height="match_parent"
        android:layout_width="wrap_content"
        android:clipToPadding="false"
        android:clipChildren="false"
        systemui:textAppearance="@style/TextAppearance.StatusBar.Clock" />
</LinearLayout>

我们接着看 StatusBarIconControllerImpl 的构造函数,因为 mStatusBarIconController 是一个接口,而其的实现类是 StatusBarIconControllerImpl。

// StatusBarIconControllerImpl.java
    @Inject
    public StatusBarIconControllerImpl(
            Context context,
            CommandQueue commandQueue,
            DemoModeController demoModeController,
            ConfigurationController configurationController,
            TunerService tunerService,
            DumpManager dumpManager) {
        super(context.getResources().getStringArray(
                com.android.internal.R.array.config_statusBarIcons),context);
        // 省略部分代码......
    }

我们可以发现状态栏 icon 加载的图标来源于framework/base/core/res/res/values/config.xml 文件在这里我们就找到了 index 和 slot 的出处,原来在初始化的时候就已经定义好了所有的 slots,然后从 framework 中加载出来,index 就是 string-array 中的顺序。

<string-array name="config_statusBarIcons">
        <item><xliff:g id="id">@string/status_bar_alarm_clock</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_rotate</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_headset</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_data_saver</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_ime</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_sync_failing</xliff:g></item>
        <item><xliff:g id="id">@string/status_bar_sync_active</xliff:g></item>
        ................
    </string-array>

然后是 StatusBarIconControllerImpl.java 这个控制器来控制 icon 的加载显示和移除。

相关文章
|
3月前
|
Java Android开发
Android面试题经典之Glide取消加载以及线程池优化
Glide通过生命周期管理在`onStop`时暂停请求,`onDestroy`时取消请求,减少资源浪费。在`EngineJob`和`DecodeJob`中使用`cancel`方法标记任务并中断数据获取。当网络请求被取消时,`HttpUrlFetcher`的`cancel`方法设置标志,之后的数据获取会返回`null`,中断加载流程。Glide还使用定制的线程池,如AnimationExecutor、diskCacheExecutor、sourceExecutor和newUnlimitedSourceExecutor,其中某些禁止网络访问,并根据CPU核心数动态调整线程数。
93 2
|
9天前
|
消息中间件 Android开发 索引
Android面试高频知识点(4) 详解Activity的启动流程
讲解Activity的启动流程了,Activity的启动流程相对复杂一下,涉及到了Activity中的生命周期方法,涉及到了Android体系的CS模式,涉及到了Android中进程通讯Binder机制等等, 首先介绍一下Activity,这里引用一下Android guide中对Activity的介绍:
25 4
|
9天前
|
Android开发 开发者
Android面试之Activity启动流程简述
每个Android开发者都熟悉的Activity,但你是否了解它的启动流程呢?本文将带你深入了解。启动流程涉及四个关键角色:Launcher进程、SystemServer的AMS、应用程序的ActivityThread及Zygote进程。核心在于AMS与ActivityThread间的通信。文章详细解析了从Launcher启动Activity的过程,包括通过AIDL获取AMS、Zygote进程启动以及ActivityThread与AMS的通信机制。接着介绍了如何创建Application及Activity的具体步骤。整体流程清晰明了,帮助你更深入理解Activity的工作原理。
16 0
|
2月前
|
Android开发
我的Android进阶修炼:安卓启动流程之init(1)
本文深入分析了Android系统中的init进程,包括其源码结构、主要功能以及启动流程的详细注解,旨在帮助读者理解init作为用户空间的1号进程在Android启动过程中的关键作用。
32 1
|
2月前
|
Android开发
解决android apk安装后出现2个相同的应用图标
解决android apk安装后出现2个相同的应用图标
175 2
|
2月前
|
XML Android开发 数据格式
Android实战经验之Kotlin中快速实现动态更改应用图标和名称
本文介绍在Android中通过设置多个活动别名动态更改应用图标和名称的方法,涉及XML配置及Kotlin代码示例。
123 10
|
2月前
|
XML 前端开发 Android开发
Android面试高频知识点(3) 详解Android View的绘制流程
View的绘制和事件处理是两个重要的主题,上一篇《图解 Android事件分发机制》已经把事件的分发机制讲得比较详细了,这一篇是针对View的绘制,View的绘制如果你有所了解,基本分为measure、layout、draw 过程,其中比较难理解就是measure过程,所以本篇文章大幅笔地分析measure过程,相对讲得比较详细,文章也比较长,如果你对View的绘制还不是很懂,对measure过程掌握得不是很深刻,那么耐心点,看完这篇文章,相信你会有所收获的。
80 2
|
2月前
|
存储 缓存 Java
Android项目架构设计问题之优化业务接口数据的加载效率如何解决
Android项目架构设计问题之优化业务接口数据的加载效率如何解决
34 0
|
2月前
|
Java Android开发 Kotlin
Android项目架构设计问题之要在Glide库中加载网络图片到ImageView如何解决
Android项目架构设计问题之要在Glide库中加载网络图片到ImageView如何解决
25 0
|
3月前
|
Java Android开发
android 设置系统时间的流程
android 设置系统时间的方法
251 2
下一篇
无影云桌面