Android SystemUI组件(05)状态栏-系统状态图标显示管理

news2024/11/15 19:28:08

该系列文章总纲链接:专题分纲目录 Android SystemUI组件


本章关键点总结 & 说明:

说明:本章节持续迭代之前章节的思维导图,主要关注下方 SystemBars分析中状态栏中的部分-系统状态图标显示&管理 即可。

1 系统状态图标显示和管理基本解读

系统状态图标区域通常位于状态栏的右侧,显示了诸如电池电量、信号强度、Wi-Fi连接、蓝牙状态、闹钟、自动旋转锁定等系统信息的图标。以下是对这一功能方面的整体描述:

  1. 状态栏的构成:状态栏通常位于屏幕的顶部,是一个水平的条状区域。它包括系统状态图标区域和通知图标区域。系统状态图标区域显示了各种系统状态信息,而通知图标区域则显示了应用程序的通知。

  2. 系统状态图标的显示:每个系统状态图标都代表了一个特定的系统状态或功能。图标通常会根据其代表的状态变化而变化,例如,当手机连接到Wi-Fi网络时,Wi-Fi图标会显示为激活状态。某些图标可能会有多种状态,例如电池图标会根据电量的多少显示不同的充电状态。

  3. 图标的交互:用户可以通过点击某些图标来打开相关的设置界面,例如点击音量图标可能会打开音量调节滑块。长按某些图标可能会触发特定的操作或显示更多的选项,例如长按Wi-Fi图标可能会显示可用的Wi-Fi网络列表。

  4. 状态栏的自定义:用户可以在系统设置中对状态栏的某些方面进行自定义,比如选择显示或隐藏某些图标。一些制造商可能还会提供额外的自定义选项,如改变状态栏的透明度或颜色。

  5. 多用户和多任务处理:在多用户环境中,状态栏可能会显示与当前活跃用户相关的信息。在多任务处理时,状态栏可以提供快速切换最近使用应用的入口。

  6. 系统状态图标的更新:当系统状态发生变化时(如连接到新的Wi-Fi网络),状态栏会自动更新相应的图标。系统状态图标的更新通常是由系统服务如StatusBarManagerService通过SystemUI来实现的。

  7. 锁屏状态下的状态栏:在锁屏状态下,状态栏可能会显示简化的信息,或者根据安全策略隐藏某些敏感信息。

  8. 沉浸式体验:在某些全屏应用中,状态栏可能会被隐藏,以提供更加沉浸式的用户体验。

整体而言,系统状态图标区域是Android系统中用户界面的一个重要组成部分,它为用户提供了快速查看和交互系统状态的便捷方式。

2 系统状态图标区域显示图标流程解读

这里的关键流程分析为:系统状态图标区域中显示一个图标的流程,主要分析StatusBarManager的setIcon方法实现。拆毁分成2个过程,具体如下:

2.1 从StatusBarManager的setIcon到mBar.setIcon详细解读

StatusBarManager的setIcon代码实现如下:

public class StatusBarManager {
    //...
    private Context mContext;
    private IStatusBarService mService;
    private IBinder mToken = new Binder();
    
    //...
    StatusBarManager(Context context) {
        mContext = context;
    }
    //...
    private synchronized IStatusBarService getService() {
        if (mService == null) {
            mService = IStatusBarService.Stub.asInterface(
                    ServiceManager.getService(Context.STATUS_BAR_SERVICE));
            if (mService == null) {
                Slog.w("StatusBarManager", "warning: no STATUS_BAR_SERVICE");
            }
        }
        return mService;
    }
    //...
    public void setIcon(String slot, int iconId, int iconLevel, String contentDescription) {
        try {
            final IStatusBarService svc = getService();
            if (svc != null) {
                svc.setIcon(slot, mContext.getPackageName(), iconId, iconLevel,
                    contentDescription);
            }
        } catch (RemoteException ex) {
            // system process is dead anyway.
            throw new RuntimeException(ex);
        }
    }
    //...
}

这个方法的作用是将一个图标添加到状态栏的指定位置,并提供图标的资源ID和描述信息。如果调用成功,状态栏上会在相应的位置显示这个图标。如果svcIStatusBarService的实例)为空或者发生了远程异常(RemoteException),则表示无法与状态栏服务通信,通常会抛出一个运行时异常。其中的4个参数解读如下:

  1. slot:这是一个字符串参数,代表了图标在状态栏中的位置或者"插槽"。状态栏可以有多个这样的"插槽"来显示不同的图标,比如信号强度、电池电量、Wi-Fi连接等。开发者可以通过指定不同的slot来控制图标在状态栏中的具体位置。

  2. iconId:这是一个整型参数,代表了要显示的图标资源的ID。它在config_statusBarIcons所预定义的意图列表之中。这个ID通常指向应用程序的资源文件中的一个图标。例如,如果你有一个表示Wi-Fi连接状态的图标,它可能在资源文件中有一个唯一的ID。

  3. iconLevel:这个整型参数通常用于表示图标的状态或者级别。例如,电池电量图标可能会使用不同的iconLevel来表示不同的电量状态。在某些情况下,这个参数可能不被使用,具体取决于图标的类型和状态栏的实现。

  4. contentDescription:这是一个字符串参数,为图标提供了内容描述。这对于辅助功能(如屏幕阅读器)非常重要,因为它们可以使用这个描述来向用户报告图标的含义。例如,如果图标表示Wi-Fi连接,contentDescription可能会设置为"Wi-Fi连接"。

实际上就是调用StatusBarManagerService的setIcon方法,代码具体实现为:

public class StatusBarManagerService extends IStatusBarService.Stub {
    private static final String TAG = "StatusBarManagerService";

    //...
    private NotificationDelegate mNotificationDelegate;
    private volatile IStatusBar mBar;
    private StatusBarIconList mIcons = new StatusBarIconList();

    //...
    public StatusBarManagerService(Context context, WindowManagerService windowManager) {
        mContext = context;
        mWindowManager = windowManager;

        final Resources res = context.getResources();
        mIcons.defineSlots(res.getStringArray(com.android.internal.R.array.config_statusBarIcons));

        LocalServices.addService(StatusBarManagerInternal.class, mInternalService);
    }

    //...
    @Override
    public void setIcon(String slot, String iconPackage, int iconId, int iconLevel,
            String contentDescription) {
        //安全性检查,必须有android.permission.STATUS_BAR系统权限才能设置系统状态图标。
        enforceStatusBar();

        synchronized (mIcons) {
            //从mIcons中获取预定义的意图索引,找不到抛异常
            int index = mIcons.getSlotIndex(slot);
            if (index < 0) {
                throw new SecurityException("invalid status bar icon slot: " + slot);
            }
            //创建StatusBarIcon对象(封装图标相关信息)
            StatusBarIcon icon = new StatusBarIcon(iconPackage, UserHandle.OWNER, iconId,
                    iconLevel, 0,
                    contentDescription);

            //这里将图标信息保存在副本中,主要用于SystemUI意外退出后恢复使用。
            mIcons.setIcon(index, icon);

            if (mBar != null) {
                try {
                    //这里设置请求发送给BaseSstatusBar,提交到SystemUI状态栏
                    mBar.setIcon(index, icon);
                } catch (RemoteException ex) {
                }
            }
        }
    }
    //...
}

接下来对mBar.setIcon的实现进行详细解读。因为这里涉及了IStatusBar,这里先简单做一个基本的解读。SystemUI是负责提供用户与系统交互界面的组件,其中包括状态栏、通知栏、锁屏界面等。在这个上下文中,CommandQueueStatusBarWindowViewBaseStatusBarPhoneStatusBar是构成状态栏的关键组件。下面是它们之间的关系和作用的解读:

  1. BaseStatusBarBaseStatusBar是一个抽象类,它定义了状态栏的基本接口和一些共用的逻辑。它作为状态栏的核心,处理与状态栏相关的各种事件和请求,比如图标的更新、通知的显示等。它实现了CommandQueue.Callbacks接口,这意味着它可以接收来自CommandQueue的命令并作出响应。

  2. PhoneStatusBarPhoneStatusBarBaseStatusBar的具体实现,它针对手机设备(而不是平板电脑)提供了状态栏的具体逻辑和UI实现。它负责创建和管理状态栏的视图,包括初始化状态栏的布局、处理用户交互(如下拉通知栏)、更新系统图标等。PhoneStatusBar在启动时会创建CommandQueue的实例,并将自己设置为命令的回调对象。

  3. CommandQueueCommandQueue继承了IStatusBar.Stub,它作为状态栏与其他系统服务(如StatusBarManagerService)通信的桥梁。它接收来自系统服务的命令,并将这些命令分发到BaseStatusBar的实现(在这个场景中是PhoneStatusBar)。CommandQueue还持有一个StatusBarIconList的引用,用于管理状态栏图标的状态。

  4. StatusBarWindowViewStatusBarWindowView是状态栏窗口的根视图组件,它是所有状态栏UI元素的容器。这个视图在PhoneStatusBarmakeStatusBarView()方法中被创建和初始化,它包含了状态栏的布局和子视图,如时钟、电池图标、信号强度指示器等。StatusBarWindowView负责在屏幕上显示状态栏,并且响应系统主题的变化(如深色模式)。

在Android 系统中,当系统需要更新状态栏(例如,收到新通知、信号强度变化等)时,StatusBarManagerService会通过CommandQueue发送相应的命令。CommandQueue将这些命令转发给PhoneStatusBarBaseStatusBar的具体实现),PhoneStatusBar根据命令更新StatusBarWindowView中的内容,从而在用户设备上反映这些变化。

有了上面的基础,我们继续分析mBar.setIcon的实现。

2.2 mBar.setIcon详细解读

这里的mBar是IStatusBar类型,实际上是CommandQueue(它继承了IStatusBar.Stub)类型。因此继续分析mBar对应类型CommandQueue的setIcon方法。具体实现如下:

public class CommandQueue extends IStatusBar.Stub {
    private static final int INDEX_MASK = 0xffff;
    private static final int MSG_SHIFT  = 16;
    private static final int MSG_MASK   = 0xffff << MSG_SHIFT;

    private static final int OP_SET_ICON    = 1;
    private static final int OP_REMOVE_ICON = 2;
    //...
    public CommandQueue(Callbacks callbacks, StatusBarIconList list) {
        mCallbacks = callbacks;
        mList = list;
    }

    public void setIcon(int index, StatusBarIcon icon) {
        synchronized (mList) {
            int what = MSG_ICON | index;
            mHandler.removeMessages(what);
            //hanlder发送消息OP_SET_ICON
            mHandler.obtainMessage(what, OP_SET_ICON, 0, icon.clone()).sendToTarget();
        }
    }

    public void removeIcon(int index) {
        synchronized (mList) {
            int what = MSG_ICON | index;
            mHandler.removeMessages(what);
            //hanlder发送消息OP_REMOVE_ICON
            mHandler.obtainMessage(what, OP_REMOVE_ICON, 0, null).sendToTarget();
        }
    }
    //...
    private final class H extends Handler {
        public void handleMessage(Message msg) {
            final int what = msg.what & MSG_MASK;
            switch (what) {
                case MSG_ICON: {
                    final int index = msg.what & INDEX_MASK;
                    final int viewIndex = mList.getViewIndex(index);
                    switch (msg.arg1) {
                        case OP_SET_ICON: {//处理消息OP_SET_ICON
                            StatusBarIcon icon = (StatusBarIcon)msg.obj;
                            StatusBarIcon old = mList.getIcon(index);
                            
                            if (old == null) {//不存在则添加图标
                                mList.setIcon(index, icon);
                                //这里的mCallbacks是初始化时传递进来的,实际上就是PhoneStatusBar
                                mCallbacks.addIcon(mList.getSlot(index), index, viewIndex, icon);
                            } else {//存在则更新图标
                                mList.setIcon(index, icon);
                                mCallbacks.updateIcon(mList.getSlot(index), index, viewIndex,
                                        old, icon);
                            }
                            break;
                        }
                        case OP_REMOVE_ICON:
                            if (mList.getIcon(index) != null) {
                                mList.removeIcon(index);
                                //这里的mCallbacks是初始化时传递进来的,实际上就是PhoneStatusBar
                                mCallbacks.removeIcon(mList.getSlot(index), index, viewIndex);
                            }
                            break;
                    }
                    break;
                }
                //...
            }
        }
    }
    //...
}

接下来我们对mCallbacks部分进行简单解读,实际上就是PhoneStatusBar在构造函数阶段初始化时传递给CommandQueue。而PhoneStatusBar又是继承自BaseStatusBar的,BaseStatusBar代码实现如下:

public abstract class BaseStatusBar extends SystemUI implements
        CommandQueue.Callbacks, ActivatableNotificationView.OnActivatedListener,
        RecentsComponent.Callbacks, ExpandableNotificationRow.ExpansionLogger,
        NotificationData.Environment {
    public static final String TAG = "StatusBar";
    public static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
    public static final boolean MULTIUSER_DEBUG = false;

    public void start() {
        //...
        // Connect in to the status bar manager service
        StatusBarIconList iconList = new StatusBarIconList();
        //这里初始化CommandQueue时传入的实际山是PhoneStatusBar
        mCommandQueue = new CommandQueue(this, iconList);
        //...
    }
    //...
}

接下来继续看PhoneStatusBar的实现与对setIcon的分别处理(addIcon和updateIcon),代码实现如下:

public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
        DragDownHelper.DragDownCallback, ActivityStarter, OnUnlockMethodChangedListener {
    static final String TAG = "PhoneStatusBar";
    public static final boolean DEBUG = BaseStatusBar.DEBUG;
    public static final boolean SPEW = false;
    public static final boolean DUMPTRUCK = true; // extra dumpsys info
    public static final boolean DEBUG_GESTURES = false;
    public static final boolean DEBUG_MEDIA = false;
    public static final boolean DEBUG_MEDIA_FAKE_ARTWORK = false;
    //...
    public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon) {
        StatusBarIconView view = new StatusBarIconView(mContext, slot, null);
        view.set(icon);
        mStatusIcons.addView(view, viewIndex, new LinearLayout.LayoutParams(
                LayoutParams.WRAP_CONTENT, mIconSize));
        view = new StatusBarIconView(mContext, slot, null);
        view.set(icon);
        mStatusIconsKeyguard.addView(view, viewIndex, new LinearLayout.LayoutParams(
                LayoutParams.WRAP_CONTENT, mIconSize));
    }

    public void updateIcon(String slot, int index, int viewIndex,
            StatusBarIcon old, StatusBarIcon icon) {
        StatusBarIconView view = (StatusBarIconView) mStatusIcons.getChildAt(viewIndex);
        view.set(icon);
        view = (StatusBarIconView) mStatusIconsKeyguard.getChildAt(viewIndex);
        view.set(icon);
    }

    public void removeIcon(String slot, int index, int viewIndex) {
        mStatusIcons.removeViewAt(viewIndex);
        mStatusIconsKeyguard.removeViewAt(viewIndex);
    }
    //...
}

至此,关键代码的流程就分析结束了。总结下

  • BaseStatusBar定义了状态栏的基本行为。
  • PhoneStatusBar提供了这些行为的具体实现
  • CommandQueue处理来自系统服务的命令
  • StatusBarWindowView则是状态栏UI的展示层。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2120447.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于Python的机器学习系列(32):PyTorch - 循环神经网络(RNN)

在本篇文章中&#xff0c;我们将探索循环神经网络&#xff08;RNN&#xff09;&#xff0c;这是一种特别适用于时间序列数据或文本数据的神经网络模型。在RNN中&#xff0c;当前的输出不仅取决于当前的输入&#xff0c;还受到前一步输出的影响&#xff0c;从而能够捕捉序列数据…

Java 入门指南:JVM(Java虚拟机)—— Java 类文件结构

文章目录 字节码JVM 与字节码字节码的生成过程 Class 文件结构魔数&#xff08;Magic Number&#xff09;Class 文件版本号&#xff08;Minor&Major Version&#xff09;常量池&#xff08;Constant Pool&#xff09;访问标志(Access Flags)前类&#xff08;This Class&…

Pygame中Sprite类实现多帧动画3-3

4 使用自定义类MySprite 使用自定义类MySprite实现多帧动画的步骤是首先创建MySprite类的实例&#xff0c;之后使用相关函数对该实例进行操作。 4.1 创建MySprite类的实例 创建MySprite类的实例的代码如图12所示。 图12 创建MySprite类的实例的代码 其中&#xff0c;变量dr…

2024年CCPC网络赛A题题解 —— 军训Ⅰ(gym105336A)

个人认为很唐的一道题&#xff0c;考虑到不少人可能懒得写&#xff0c;我这里给大家发个代码叭&#xff0c;还有一点点题解&#xff08;因为真的不是很难&#xff09;。这是题面&#xff1a; 然后我来讲讲怎么做&#xff0c;不觉得会有多少人题目意思都理解不了叭&#xff1f;这…

Javaweb项目-调用接口-如何在服务器端跳转网页后显示并弹出对话框代码

Webapp 项目中在java包下新建一个服务端类 使用JOptionPane框架组件 调用showMessageDialog的方法实现 四个参数null,"这是一个信息对话框","信息",JOptionPane.INFORMATION_MESSAGE 还有确认对话框的代码showConfirmDialog package servlet;import java…

k9s 是什么?有什么功能?

目录 k9s 是什么&#xff1f; 有什么功能&#xff1f; 手动安装 K9s&#xff08;Windwos&#xff09; 将 k9s.exe 添加到系统 PATH 启动 K9s k9s 是什么&#xff1f; K9s 是一个命令行工具&#xff0c;用于通过一个图形化的终端界面&#xff08;类似于图形化用户界面但在命…

【Linux】常用的命令

文章目录 lsls -l / touchcdpwdcatechovim打开文件编辑内容保存退出 mkdirrmmvcpmangreppsnetstat总结 &#xff1a; ls ls > list 列出当前目录下都有哪些内容&#xff08;文件/目录&#xff09; 直接输入 ls&#xff0c;是查看当前目录的情况&#xff1b;输入 ls/ 就是看…

检查你的防病毒软件是否可以阻止这 5 个测试恶意软件文件

从网络安全专家到你&#xff0c;每个人都知道你应该使用防病毒软件来保护你的电脑免受黑客、病毒和其他类型的网络威胁。 但即使你这些年来一直在努力使用防病毒程序&#xff0c;你怎么知道它真的有效呢&#xff1f; 安全专家已经想到了这一点&#xff0c;并创建了几种类型的…

TMS320F28335芯片及使用介绍

1、简介 CPU性能的好坏不仅取决于主频大小,还需要看其整体架构集成性能、运算能力与指令体系。TMS320C2000系列DSP集微控制器和高性能 DSP 的特点于一身,具有强大的控制和信号处理能力,能够实现复杂的控制算法。TMS320C2000 系列DSP 片上整合了Flash存储器、快速的AD转换器…

基于微信小程序+Java+SSM+Vue+MySQL的付费自习室预订管理系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、SSM项目源码 系统展示 基于微信小程序JavaSSMVueMySQL的付费自习室预订管理系统【…

【CMake编译报错小复盘】CMAKE_CUDA_ARCHITECTURES,CMake version,GCC version问题

今天在写大模型量化推理框架时遇到了一些编译上的错误&#xff0c;简单复盘一下问题和解决方案&#xff1a; 问题1&#xff1a;CMAKE_CUDA_ARCHITECTURES 报错信息&#xff1a; CMake Error: CMAKE_CUDA_ARCHITECTURES must be non-empty if set cmake和cuda相关的报错通常都…

linux进程间通信——进程间通信概念、最基本通信——管道文件

前言&#xff1a; 本节内容将要讲解进程间通信。 之前我们说过进程之间是相互独立的&#xff0c; 但是&#xff0c;相互独立并不代表不能进行数据的输送。就好比我和你是相互独立的&#xff0c; 但是我们可以成为朋友&#xff0c; 可以互赠礼物。 而我们一般而言的&#xff0c;…

【C语言】归并排序递归和非递归——动图演示

目录 一、归并排序思想1.1 基本思想1.2 大体思路 二、实现归并排序&#xff08;递归&#xff09;三、实现归并排序&#xff08;非递归&#xff09;3.1 实现思路&#xff1a;3.2 越界处理3.3 时间复杂度和空间复杂度 总结 一、归并排序思想 1.1 基本思想 归并排序&#xff08;M…

RTMP和WebRTC使用场景有哪些差别?

省流版先说结论 直播领域&#xff0c;RTMP和WebRTC各有优势。如果直播场景对延迟有一定要求&#xff0c;但更注重稳定性和兼容性&#xff0c;那么RTMP可能是一个更好的选择。如果直播场景需要极低的延迟&#xff0c;并且用户主要在浏览器环境下进行观看和互动&#xff0c;那么…

Leangoo敏捷工具在缺陷跟踪(BUG)管理中的高效应用

在开发过程中&#xff0c;缺陷&#xff08;BUG&#xff09;管理一直是项目管理中的一个关键环节。及时发现并修复BUG&#xff0c;不仅能够提高产品质量&#xff0c;还能有效提升团队的工作效率和用户满意度。 在敏捷开发中&#xff0c;快速迭代和频繁交付的特点使得缺陷管理的…

Servlet的特性(一)

Servlet的主要用途&#xff1a; 接受、处理来自浏览器端(BS架构中的B端)的请求和用户输入 响应来自数据库或者服务端(BS架构中的S端)产生的数据到浏览器端&#xff0c;动态构建网页。 手动实现Servlet小程序 实现步骤 自定义一个类型&#xff0c;实现Servlet接口或者继承Ht…

Spring Boot 集成 Redisson 实现消息队列

包含组件内容 RedisQueue&#xff1a;消息队列监听标识RedisQueueInit&#xff1a;Redis队列监听器RedisQueueListener&#xff1a;Redis消息队列监听实现RedisQueueService&#xff1a;Redis消息队列服务工具 代码实现 RedisQueue import java.lang.annotation.ElementTyp…

GD32E230 RTC报警中断功能使用

GD32E230 RTC报警中断使用 GD32E230 RTC时钟源有3个&#xff0c;一个是内部RC振动器产生的40KHz作为时钟源&#xff0c;或者是有外部32768Hz晶振.,或者外部高速时钟晶振分频作为时钟源。 &#x1f516;个人认为最难理解难点的就是有关RTC时钟异步预分频和同步预分频的计算。在对…

C++第二节入门 - 缺省参数和函数重载

一、缺省参数 1、概念 缺省参数是声明或定义函数时为函数的参数指定一个缺省值。 在调用该函数的时候&#xff0c;如果没有指定实参则采用该形参的缺省值&#xff0c;否则使用指定的实参&#xff01; #include<iostream> using namespace std;void Func(int a 0) {c…

2024 水博会,国信华源登场,数智创新助力水利高质量发展

9月4日-6日&#xff0c;由中国水利学会和中国水利工程协会共同主办的2024中国水博览会暨第十九届中国&#xff08;国际&#xff09;水务创新技术交流会在重庆国际博览中心召开。 本次水博会以“展水利前沿新技术 览新质生产力场景”为主题&#xff0c;国信华源携最新智能监测预…