车机开发【Android SystemUI 架构音量控制详解】

news/2024/4/20 8:49:00/文章来源:https://blog.csdn.net/m0_62167422/article/details/129150502

SystemUI介绍

SystemUI摘要

在Android系统中SystemUI是以应用的形式运行在Android系统当中,即编译SystemUI模块会生产APK文件,源代码路径在frameworks/base/packages/SystemUI/,安装路径system/priv-app/-SystemUI。

什么是SystemUI

在前文1.1章节中可知,SystemUI是一个普通的APK文件,即是一个普通的APP,但是,手机使用者看见的所有SystemUI的内容都不像是一个APP,为什么?既然是一个应用,就是完成特定的功能,SystemUI主要完成的功能有:

  • (1)Status bars
  • (2)Navigation bars
  • (3)Notification
  • (4)Lockscreen
  • (5)Quick settings
  • (6)Overview(recent task switcher)
  • (7)VolumeUI

SystemUI的SERVICES

音量控制

音量控制简介

如图章节1.2中的VolumeUI所示,当用户操作音量键时,会弹出相应的UI显示,并可以设置音量大小和情景模式。VolumeUI的主要代码在SystemUI/volume下。在不同模式下,音量键触发的UI显示样式不一样,分别是通话、铃声(通知)、音乐、闹铃、蓝牙输出等,如下图 9-13

音量控制SERVICE的初始化

在第二章节中SystemUI的启动过程提到,SystemUI的所有Service通过SystemUI类的start()方法启动,并且通过图7可以知道,volume service的VolumeUI继承了SystemUI类,所以start()实质是执行VolumeUI中的方法,如下图:

如上图中的代码,首先读取VolumeUI的开关,如果mEnabled为true,则调用initPanel()方法实例化UI等控件元素(如图15),实例化VolumeController控制器(如图17),调用putComponent()保存对象实例,调用updateController()设置控制器(如图18)。

上图中主要是new一个VolumePanel对象,VolumePanel是Handler的子类,且又是VolumeUI的Pannel,因此,VolumePanel负责绘制VolumeUI的内容和控制VolumeUI的显示。先看看VolumeUI的创建过程:

从上图中的代码可见,VolumeUI是以Dialog的形式显示UI,VolumePanel的实例化过程创建Dialog实例和初始化ZenModePanel,到此VolumePanel将会待命。上文中提到VolumePanel同时是Handler的子类,一旦VolumePanel收到相关的Message时,将会处理UI的显示和关闭。

上图是VolumeController的实现代码,主要实现对VolumeUI的Panel的控制,例如上图中的volumeChanged()控制Panel的显示和变化,dismissNow()控制Panel的关闭。那么VolumeController是被谁管控呢?如下图:

图中可以看到先通过设置Provider读取是否允许systemui控制volume,如果允许,则设置通过AudioManager的实例设置VolumeController到AudioService(读者如果不了解这个过程,可以自行阅读Android Audio策略)。至此,VolumeUI的初始化全部完成。 通过本节的学习,VolumeUI的架构如下图:

控制音量过程

当SystemUI的VolumeUI当前不是活动窗口时,一般情况下,音量的设置是通过音量键进行操作,当用户操作音量键时,如果用户不拦截音量键事件,那么默认音量键的事件将会在Window中被消化,Window将捕获到的音量键事件通过Binder机制将音量变化信息传送到MediaSessionService,MediaSessionService同样通过Binder机制接着传送到AudioService,最后AudioService也同样通过Binder机制把信息传送给SystemUI(VolumeUI),VolumeUI将会作出相应的变化。下面将详细了解这个过程: 当手机设备当前活动窗口在Laucher桌面,Laucher没有对音量键事件作拦截操作,音量键事件将会在PhoneWindow中被消化。在Android的单次点击事件中,分down和up两种事件,分别被分发到PhoneWindow的onKeyUp()和onKeyDown()方法中,如下图20-21:

图20是消化down事件,图21是消化up事件,但音量键还有上音量键(+)和下音量键(-),从这两张图可以看到,KEYCODE_VOLUME_UP没有作任何处理,上音量键(+)的事件会在下音量键中消化(-), 在down和up事件中都是调用sendAdjustVolumeBy()同一个方法,传递三个参数,第一个参数是指定音量类型,mVolumeControlStream为默认值,取值Integer.MIN_VALUE,图20和图21相同,第二个是delta,音量控制类型,即增加或减少,图20传递direction,图21传递0,direction取值1或-1,即1:增加、0:不变、-1:减少,第三个参数是flags,控制VolumeUI显示,每个参数具体的控制的实现代码将在下文中描述。继续跟踪流程,Laucher进程通过Binder机制把信息传送到MediaSessionService,如下图:

上图中有获取MediaSessionRecord的对象来控制音量,这里的session变量的值是null,如果读者对此感兴趣可自行阅读相关资料。继续看dispatchAjustVolumeLocked()方法:

图23中可以知道参数只是多了一个packageName,其它的都是图20或图21中的参数值。接着往下看:

在图20或图21中有描述suggestedStreamType的值是Integer.MIN_VALUE,在上图中通过getActiveStreamType()方法对值进行转换,变成streamType,它的取值可能是0到10,分别控制不同类型的音量,如3.1.1章节中所以,本例子streamType的值是2,即调整的是铃声(通知)的音量。接着又调用了adjustStreamVolume()方法,如下:

adjustStreamVolume()方法对direction和streamType的合法进行校验,direction取值-1到1,streamType取值0到10。之后通过mStreamState获取oldIndex、newIndex和index值,其中oldindex和index作为sendVolumeUpdate()方法的参数,将会影响音量变化的广播和AudioProfile,关于AudioProfile读者感兴趣可以自行学习。图中还可以看到这里还设置了HDMI接口输出。继续看sendVolumeUpdate()方法:

在sendVolumeUpdate()方法中处理了几个事件,一个postVolumeChanged()方法,最终通知SystemUI,后面赘述。接着发送注册到AudioManager.VOLUME_CHANGED_ACTION的action的广播,通知音量改变并携带音量大小的原值oldIndex和新值index,最后通知AudioProfile。继续看postVolumeChanged()方法:

这里通过mControlle对象调用volumeChanged(),mController实质是一个什么类的实例,回顾3.1.2章节中的图18,updateController()方法设置了VolumeController的实例,因此mController正是VolumeController在AudioService中的句柄,通过Binder机制,把音量变化的信息从AudioService传输到SystemUI进程。转移到SystemUI,如下图:

从AudioService回调到volumeChanged()方法,接着调用mPanel的postVolumeChanged()方法,mPanel在前文3.1.2章节的图15中有描述,是VolumePanel的实例,前文中提到,VolumePanel是Handler的子类,也是VolumeUI的Panel,下面结合代码分析VolumePanel的具体功能:

上文提到VolumePanel是Handler的子类,图29中VolumePanel将发送MSG_VOLUME_CHANGED的Message到自身持有的线程,接着看MSG_VOLUME_CHANGED的代码:

上图中直接又调用了onShowVolumeChanged()方法,顾名思义是显示音量变化的UI,后面接着继续调用resetTimeout()方法,先跟踪onShowVolumeChanged()方法:

上图中首先调用getStreamVolume()方法获取对应streamType当前的音量值,通过StreamControl匹配streamType的UI,StreamControl是一个容器,在VolumeUI的初始化时被实例化,装载不同streamType的UI配置,并保存到mStreamControls数组对象中,因此streamType的值确定了StreamControl的类型,StreamControl确定了Dialog显示的UI类型。确定StreamControl的类型后,调用updateSliderProgress()方法更新界面控件,最后调用mDialog.show()方法绘制界面。至此,从按下音量键到调用mDialog.show(),设备对点击事件作出相应,并显示相应的UI到界面上。 回顾图31,resetTimeout()方法的实现如下:

这里会延时发送一个空消息到VolumePanel,what为MSG_TIMEOUT,mTimeoutDelay的值为3000,跟踪MSG_TIMEOUT的处理过程:

很简单,实现的功能就是在VolumeUI显示后,延时3秒自动把VolumeUI关闭。 到此SystemUI的VolumeUI service分析完毕,VolumeUI的流程简单清晰,代码简洁,阅读方便。回顾VolumeUI整个控制流程,可用下图总结:

上图中PhoneWindow是当前活动窗口的进程的PhoneWindow实例捕获音量键事件,当前活动窗口的可以通过自身的PhoneWindow对象实例设置streamType,即控制音量键事件触发的VolumeUI类型。更多车机开发学习资料;可参考《车载技术开发手册》里面记录了大部分车机进阶文档。需要可以点击查看。

结尾

当应用程序拦截音量键事件,那么PhoneWindow将无法捕获到音量键事件,此时音量键事件将不遵行上图的流程。如果当前活动窗口时SystemUI,则直接由SystemUI所在进程的活动窗口的PhoneWindow对象实例获取到音量键事件,这时音量键事件的转发和处理和在其它进程(如Laucher)中略有不同。

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

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用带有 Moveit 的深度相机来避免碰撞

文章目录 什么是深度相机?如何将 Kinect 深度相机添加到您的环境中在 Rviz 中可视化深度相机数据在取放场景中使用深度相机将深度相机与您的 Moveit 设置一起使用有很多优势。机器人可以避免未知环境中的碰撞,甚至可以对周围的变化做出反应。然而,将深度相机连接到您的设置并…

FlinkSQL行级权限解决方案及源码

FlinkSQL的行级权限解决方案及源码,支持面向用户级别的行级数据访问控制,即特定用户只能访问授权过的行,隐藏未授权的行数据。此方案是实时领域Flink的解决方案,类似离线数仓Hive中Ranger Row-level Filter方案。 源码地址: https…

数据分片(mycat)

1. 数据分片概念: 1.1. 分库分表 什么是分库分表: 将存放在一台数据库服务器中的数据,按照特定方式(指的是程序开发的算法)进行拆分,分散存放到多台数据库服务器中,以达到分散单台服务器负载的…

第51篇-某彩网登录参数分析-webpack【2023-02-21】

声明:该专栏涉及的所有案例均为学习使用,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关!如有侵权,请私信联系本人删帖! 文章目录 一、前言二、网站分析一、前言 今天我们看一个webpack的网站 aHR0cHM6Ly8xMGNhaTUwMC5jYy9sb2dpbg==二、网站分析 首先…

网络协议(一)应用层(自定制协议、HTTP协议)

目录 应用层:负责应用程序之间的数据沟通 一、自定制协议(私有协议) 二、HTTP协议 1)、请求行解析:GET /index.html HTTP/1.1 第一部分:请求方法:多种多样,描述不同的请求目的 …

大数据知识图谱项目——基于知识图谱的医疗知识问答系统(详细讲解及源码)

基于知识图谱的医疗知识问答系统 一、项目概述 本项目基于医疗方面知识的问答,通过搭建一个医疗领域知识图谱,并以该知识图谱完成自动问答与分析服务。本项目以neo4j作为存储,基于传统规则的方式完成了知识问答,并最终以关键词执…

Verilog 学习第五节(串口发送部分)

小梅哥串口部分学习part1 串口通信发送原理串口通信发送的Verilog设计与调试串口发送应用之发送数据串口发送应用之采用状态机实现多字节数据发送串口通信发送原理 1:串口通信模块设计的目的是用来发送数据的,因此需要有一个数据输入端口 2:…

Qt中修改界面类的类名时需要注意的几个修改点

有些时候因为一些原因,需要修改Qt中创建的界面类,需要特别注意几个修改点。 比如将test类修改为test2类 修改test.h名称为test2.h文件;修改test.cpp名称为test2.cpp文件;修改test.ui名称为test2.ui文件;修改pro文件中…

多层感知机的区间随机初始化方法

摘要: 训练是构建神经网络模型的一个关键环节,该过程对网络中的参数不断进行微调,优化模型在训练数据集上的损失函数。参数初始化是训练之前的一个重要步骤,决定了训练过程的起点,对模型训练的收敛速度和收敛结果有重要…

Java基础43 异常(Exception)

异常(Exception)Exception1.1 异常的概念1.2 异常体系图(☆)1.3 异常处理分类1.3.1 运行时异常(☆)1.3.2 编译时异常(☆)1.4 异常处理(☆)1.4.1 try-catch异常…

【Git】Git下载安装与使用(一)

目录 1. 前言 1.1 什么是Git 1.2 使用Git能做什么 2. Git概述 2.1 Git简介 2.2 Git下载与安装 3. Git代码托管服务 3.1 常用的Git代码托管服务 3.2 码云代码托管服务 1. 前言 1.1 什么是Git Git是一个分布式版本控制工具,主要用于管理开发过程中的源代码…

Cookies与Session会话技术详解

引言:日常生活中,人和人之间沟通交流,涉及到一个词----会话,软件中一样存在会话,如:网购登录,访问公司OA系统也是不断的会话,软件中如何管理浏览器客户端和服务端之间会话过程中的会话数据呢&am…

盘点四种自动化测试模型实例及优缺点

一,线性测试 1.概念: 通过录制或编写对应应用程序的操作步骤产生的线性脚本。单纯的来模拟用户完整的操作场景。 (操作,重复操作,数据)都混合在一起。 2.优点: 每个脚本相对独立&#xff0…

【java】java sftp传输 ,java smb传输访问共享文件夹 集成springboot

文章目录java的sftp传输sftp注意事项java smb传输smb注意事项tips: 集成springboot与不集成springboot区别不大,springboot中无非是引入一个maven依赖 加一个Component注解 , 默认是单例; 复制代码前 请先认真看注意事项 java的sftp传输 依赖…

网络安全态势感知研究综述

摘要:随着物联网、云计算和数字化的迅速发展,传统网络安全防护技术无法应对复杂的网络威胁。网络安全态势感知能够全面的对网络中各种活动进行辨识、理解和预测。首先分别对态势感知和网络安全态势感知的定义进行了归纳整理,介绍了网络安全态…

从0探索NLP——导航帖

从0探索NLP——导航帖 人工智能是一个定义宽泛、知识组成复杂的领域,而NLP是人工智能领域中的一类任务,他在哪呢?Emmmmm~不能说都有涉猎只能说全都都沾点: 每次想要针对NLP的某一点进行讲解时,不讲那写细枝末节&…

全链路压力测试

压力测试的目标: 探索线上系统流量承载极限,保障线上系统具备抗压能力 复制代码 如何做全链路压力测试: 全链路压力测试:整体步骤 容量洪峰 -》 容量评估 -》 问题发现 -》 容量规划 全链路压力测试:细化过程 整体目…

YOLOv6-3.0-目标检测论文解读

文章目录摘要算法2.1网络设计2.2Anchor辅助训练2.3自蒸馏实验消融实验结论论文: 《YOLOv6 v3.0: A Full-Scale Reloading 》github: https://github.com/meituan/YOLOv6上版本参考 YOLOv6摘要 YOLOv6 v3.0中YOLOv6-N达到37.5AP,1187FPS&…

linux下安装minio

获取 MinIO 下载 URL:访问:https://docs.min.io/ 一,进入/opt 目录,创建minio文件夹 cd /optmkdir minio二,wget下载安装包 wget https://dl.minio.io/server/minio/release/linux-amd64/minio三,进入minio文件夹创建…

如何使用 API 工具做 Websocket 测试

在 API 测试中,对 Websocket 协议的支持呼声越来越高,今天给大家推荐一款 开源的 API 管理工具——Postcat,以及教教大家,如何利用 API 管理工具做 Websocket 测试。 在线 Demo 链接:Postcat - Open Source API Ecosys…