系统音量条相关

1、音量键处理

image-20200718161014564.png

如上,对于一般场景下,按音量上/下键,其事件传递到焦点窗口的根节点布局时(一般的Activity根节点布局都是DecorView)会走到DecorView的dispatchKeyEvent方法中,这里主要有两个分支,一个是Window.Callback类型的cb处理事件(这里如果Activity有处理音量键事件并返回true,则具体音量事件逻辑看应用自身逻辑,如果对应Activity在处理音量键逻辑中什么都不做,那音量键相当于也没有什么实际效果,也不会弹出系统音量条),还有一个分支则是PhoneWindow中调用onKeyDown和onKeyUp处理事件,这里也是一般的按音量键弹出音量条所走的逻辑

image-20200718163848255.png
image-20200718164021778.png

如上所示,在PhoneWindow的onKeyDown和onKeyUp方法中,对于音量上/下键事件的处理是调用MediaSessionManager的dispatchVolumeKeyEventAsSystemService方法,MediaSessionManager的服务端是MediaSessionService,MediaSessionManager的前面的方法调用都会调用到其服务端MediaSessionService中,在一般显示系统音量条过程中,MediaSessionService中的主要作用是构建调用AudioService调节音量的方法(包括调节的通道、调节的粒度和flags构建等),然后在AudioService中才是真正调节音量的主要代码,在音量调节中如果音量有改变,则会发送"android.media.VOLUME_CHANGED_ACTION"的广播通知音量变更,在音量调节后会调用sendVolumeUpdate方法去告知SystemUI音量变更

image-20200718172628942.png

这里调用了VolumeController的postVolumeChanged,其中VolumeController是AudioService的静态内部类

image-20200718202148663.png

VolumeController的postVolumeChanged方法即调用了其mController变量的volumeChanged方法,这里mController变量是调用VolumeController的setController方法进行设置的

image-20200718202306217.png

在AudioService的setVolumeController方法中会调用VolumeController的setController方法

image-20200718202420757.png

在AudioManager中也有setVolumeController,其会调用到AudioService的setVolumeController方法,而在SystemUI中有调用该方法,其设置的VolumeController是VolumeDialogControllerImpl的内部类VC的对象

所以上述音量变更后通知SystemUI既是调用了VolumeDialogControllerImpl的内部类VC的volumeChanged方法回调

2、系统音量条显示逻辑

image-20200724224905501.png
2.1、音量条显示逻辑

在前面AudioService中音量变更后回调到SystemUI的VolumeDialogControllerImpl.VC的volumeChanged时,即会触发显示系统音量条的逻辑(注意,这里有传入两个参数,streamType和flags),然后会调用到VolumeDialogControllerImpl的onVolumeChangedW方法中

image-20200724201644934.png

这里会根据flags和当前场景、一些定制来判断是否显示系统音量条,其中flags相关主要是需要包含AudioManager.FLAG_SHOW_UI,如果判断需要显示,则最后会调用到VolumeDialogImpl.java中的onShowRequested方法中去显示系统音量条

image-20200724202243363.png

onShowRequested方法中会调用showH方法去显示系统音量条

image-20200724202433129.png

在showH方法中有两处比较重要,一是rescheduleTimeoutH方法,该方法是计算系统音量条自动消失时间,发送延时消息去dismiss音量条,二是mDialog.show,在SystemUI启动时就已准备好了mDialog,在音量相关变更时会更新相关信息,然后在这里只需调用mDialog.show方法即会去显示系统音量条

image-20200724211928846.png
2.2、音量条布局

在SystemUI启动时,会调用到VolumeDialogImpl的init方法初始化,其中initDialog方法即是对音量条的显示的重要部分,这里会创建mDialog并加载对应布局和设置显示相关参数,其中加载具体各通道音量条的相关代码如下

image-20200724214506628.png

这里会对各通道的音量调用addRow方法,以将各通道的音量布局加载添加到mDialog布局中

image-20200724214732911.png

在initRow方法中会根据传入的参数构建VolumeRow对象,其中会添加布局等相关信息

2.2.1、音量通道布局的显示与隐藏

前面可以看到,音量条一般有加载七个通道的布局,但其实一般的话可能只会显示一个通道的音量

在构建mDialog和音量相关变更时(onStateChanged)会触发updateRowsH方法调用

image-20200724220752911.png

在updateRowsH方法中会遍历各通道的VolumeRow,调用shouldBeVisibleH方法来判断是否需要显示对应通道的音量条,然后设置对应控件可见性为VISIBLE或GONE

在音量相关变更时,会更新相关音量信息如音量大小等,然后会触发onStateChanged调用,其会调用到VolumeDialogImpl的onStateChangedH方法中更新视图相关,包括控件可见性,其也会调用updateVolumeRowH方法来更新各通道控件的信息如seekbar的进度

2.3、音量通道

前面有介绍系统音量条一般有加载七个不同通道的音量条,但其实通道并不止七个

image-20200724222036667.png

其定义都在AudioSystem中

image-20200724222302846.png

虽然有11个通道,但因为有些通道变更都是一起的,在音量变更时会有个映射,传到SystemUI的是其映射后的通道(其实在音量变更时,即使设置单个通道音量变更,其也会影响映射相关的多个通道)

image-20200724222521284.png

其中mStreamVolumeAlias映射关系是在AudioService中赋值的

image-20200724222806145.png

比如一般手机是PLATFORM_VOICE,则mStreamVolumeAlias则是STREAM_VOLUME_ALIAS_VOICE

image-20200724223455799.png

上面即可看出其映射关系

2.4、activeStream

在AudioService中有个getActiveStreamType方法,其可获取当前activeStream,同一时间只有一个activeStream,一般使用中的音频通道即为activeStream,如播放音乐时一般activeStream是媒体通道STREAM_MUSIC,但也有其他情况,该方法中也说明了在多个音频通道同时播放或无音频播放时的activeStream

3、安全音量

image-20200725200542853.png

在插入耳机后在调大音量时可能会弹出如上Dialog,在通过AudioService调节音量时会调用checkSafeMediaVolume方法

image-20200725203104454.png

在一些特定设备如有线耳机等,且调节的音量通道是媒体通道,且调节音量大于安全音量时,checkSafeMediaVolume方法返回false,此时会调用AudioService$VolumeController的postDisplaySafeVolumeWarning方法

image-20200725203751056.png

然后其流程与volumeChanged差不多,其会调用到SystemUI的VolumeDialogImpl的showSafetyWarningH方法来显示上面提示

image-20200725203947694.png

4、自定义音量按键事件处理

很多视频播放界面都会自定义音量键处理和音量控件,其主要包括两部分,一是获取音量键事件,并调节音量,且注意应不让系统音量条显示,二是监听音量变更实现自定义的音量显示控件

image-20200727220151731.png

如上,自己本地写了个小demo,在拦截音量上/下键并自己处理返回true后,在该界面按音量上/下键就不会弹系统音量条了(注意,这里adjustStreamVolume是调节音量的api,其第二个参数如果包含了AudioManager.FLAG_SHOW_UI那么仍是会弹出系统音量条的)

image-20200727221711842.png

如上自己本地demo中写了个简单的seekbar的控件,然后监听了"android.media.VOLUME_CHANGED_ACTION"广播,收到广播后刷新seekbar进度

当然,如果需要音量控件是可滑动调节的,那么还需要监听对应控件的change事件,然后可调用AudioManager的setStreamVolume方法来设置音量

5、RingerMode

Android有三种响铃模式:响铃、振动、静音,在音量条上方按钮点击可切换不同模式,其在不同模式下主要会影响铃声相关的声音,如通知等

image-20200801161505257.png

先查看下点击切换响铃模式按钮相关逻辑,响铃按钮是mRingerIcon,其点击事件如下

image-20200801161340942.png

这里主要切换响铃模式逻辑是其中根据当前的模式和机器相关情况,计算新的响铃模式,然后调用mController.setRingerMode方法来设置新的响铃模式,其中mController是VolumeDialogControllerImpl的对象,其最后会调用到AudioManager的接口来设置新的响铃模式,这里按照上面逻辑会调用其setRingerModeInternal方法

image-20200801162528162.png

AudioManager会直接调到AudioService中

image-20200801162915940.png

这里主要会调用到AudioService的setRingerMode方法

image-20200801163446151.png

其主要代码有三部分

一是setRingerModeInt方法,这里是主要部分逻辑,放在后面查看

二是mRingerModeDelegate相关回调,这里mRingerModeDelegate其实是ZenModeHelper的内部类RingerModeDelegate的对象,这里回调会记录响铃模式和可能调整勿扰模式

三是调用setRingerModeExt方法,这里会发送"android.media.RINGER_MODE_CHANGED"的广播

这里继续查看setRingerModeInt方法:

image-20200801165201500.png

这里逻辑也比较多,简要介绍下

1、muteRingerModeStreams,该方法是这里主要部分逻辑,也是响铃模式切换的主要逻辑,在该方法中会根据响铃模式和其他如勿扰模式、设备等相关情况,判断是否需要将一些通道静音,如一般情况下,从响铃模式切换到振动或静音模式时,会将铃声相关通道mute,音量设为0,而切回响铃模式时,又会将音量设回去

2、如果persist参数为true,则一般会将新响铃模式设置到Settings.Global.MODE_RINGER

3、如果响铃模式变更,这里会发送"android.media.INTERNAL_RINGER_MODE_CHANGED_ACTION"广播

5.1、音量调节触发RingerMode变更及VolumePolicy

在调节铃声音量时,我们可能注意到,在响铃模式,将铃声音量调制最低时,会自动变为振动模式,而在振动模式,将铃声音量调高时,会自动退出振动模式变为响铃模式

这是因为在调节音量时会有相关检查

在设置音量时,其在AudioService中会触发onSetStreamVolume调用

image-20200801172241812.png

其中getNewRingerMode一般会根据铃声音量来计算新的响铃模式

image-20200801172418078.png

在调节音量时也可能会触发checkForRingerModeChange方法调用

image-20200801172845958.png

这里未将checkForRingerModeChange方法全部贴出来,其也是会根据当前相关情景调节响铃模式

在上面一些判断中,除了手机有没有振动器等的判断,还有mVolumePolicy的变量的判断,VolumePolicy是个单独的类,其主要是保存有几个变量

image-20200801174249287.png

一般这个volumepolicy是SystemUI调用setVolumePolicy有设置的,

image-20200801173739711.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 193,968评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,682评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,254评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,074评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,964评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,055评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,484评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,170评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,433评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,512评论 2 308
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,296评论 1 325
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,184评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,545评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,150评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,437评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,630评论 2 335