本文章的目标是首先了解Android中音频焦点的基本概念,理解代码中相关音频焦点的使用方法。其次理解AAOS 中相关交互矩阵概念,理解其实现焦点管理的流程。
音频焦点的目标是 是确保在多个应用程序同时播放音频时,用户能够顺利地听到他们想要听的声音,并防止多个应用同时混合输出声音,造成用户困扰。也就是每次要去播放某个声音的时候 先去请求焦点,请求到焦点 后才能开始播放。
音频焦点是在Android API 8中引入的一个概念。它用于表示用户一次只能专注于一个音频流,比如听音乐或播客,但不能同时进行。在某些情况下,多个音频流可以同时播放,但用户只会真正聆听(专注于)其中一个,而其他音频在后台播放。例如,在导航提示播放时,同时降低音量播放音乐。当应用程序请求音频焦点时,它表示希望“拥有”音频焦点来播放音频。
android 的audioManager 提供了requestAudioFocus 的接口来获取焦点
AudioFocusRequest mFocusRequest = new AudioFocusRequest.Builder(focusRequest)
.setAudioAttributes(mAttrib)
.setOnAudioFocusChangeListener(mFocusListener)
.setForceDucking(false)
.setWillPauseWhenDucked(false)
.setAcceptsDelayedFocusGain(false)
.build();
ret = mAudioManager.requestAudioFocus(mFocusRequest);
mFocusRequest 的构造有几个参数可以传递
当然还支持其他不同的参数设置,这些参数设置是为了满足不同的场景来使用。比如:在用户听音频书籍或播客时,设备播放导航提示时,用户希望音频暂停而不是降低音量,因为同时听导航提示和语音内容会让人难以理解。因此,系统不会自动对播放音频书籍或博客的应用程序进行降低音量。 如果应用程序想要需要暂停而不是降低音量,可以使用Builder.setWillPauseWhenDucked(true) 设置之后 在其他应用占用了焦点后框架不会自动为应用降低音量而是回调到应用注册的函数中。
不同类型的焦点请求:
AAOS请求处理是CarService 这边处理,所谓的处理 就是根据传递进来的请求参数 和目前持有的场景来决定 是否让这个焦点请求成功。 这个具体的规则是基于交互矩阵来实现的。具体来所通过重写 AudioPolicy的相关回调 来截获原来的audioRequest,然后处理完成后设置到AudioManger 中。主要是重写下面的那些实现。
public static abstract class AudioPolicyFocusListener {
public void onAudioFocusGrant(AudioFocusInfo afi, int requestResult) {}
public void onAudioFocusLoss(AudioFocusInfo afi, boolean wasNotified) {}
/**
* Called whenever an application requests audio focus.
* Only ever called if the {@link AudioPolicy} was built with
* {@link AudioPolicy.Builder#setIsAudioFocusPolicy(boolean)} set to {@code true}.
* @param afi information about the focus request and the requester
* @param requestResult deprecated after the addition of
* {@link AudioManager#setFocusRequestResult(AudioFocusInfo, int, AudioPolicy)}
* in Android P, always equal to {@link #AUDIOFOCUS_REQUEST_GRANTED}.
*/
public void onAudioFocusRequest(AudioFocusInfo afi, int requestResult) {}
/**
* Called whenever an application abandons audio focus.
* Only ever called if the {@link AudioPolicy} was built with
* {@link AudioPolicy.Builder#setIsAudioFocusPolicy(boolean)} set to {@code true}.
* @param afi information about the focus request being abandoned and the original
* requester.
*/
public void onAudioFocusAbandon(AudioFocusInfo afi) {}
}
AAOS中的音频焦点管理具体外部设置focus流程:
为了支持 AAOS 的需求,系统会根据请求的 CarAudioContext
和当前焦点持有者的 CarAudioContext 之间的预定义交互来处理音频焦点请求。交互类型分以下三种:独占、拒绝和并发。
独占交互
简单来说就是当前应用持有的焦点会被将要请求焦点的应用占有,当前应用失去焦点。
拒绝交互
简单来说当前应用持有的焦点会一直保持,其他应用无法获取当前的焦点。
并发交互
当前应用和其他应用可以同时拥有焦点,AAOS特有的。
AAOS 最独特的地方就是并发交互。在这种交互模式下,请求音频焦点的车载应用可与其他应用同时持有焦点。若要实现并发交互,必须满足以下条件。即:
传入的焦点请求的是 AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK
。
当前焦点持有者未设置 setPauseWhenDucked(true)
。
如果满足上述条件,焦点请求将返回 AUDIOFOCUS_REQUEST_GRANTED
,而当前焦点持有者的焦点不会发生任何变化。不过,如果当前焦点持有者选择接收闪避事件或在闪避时暂停,则会失去焦点,就像独占交互一样。
下表显示了由 CarAudioService
定义的交互矩阵。行内容和列内容分别表示当前焦点持有者和传入请求的 CarAudioContext
。
举例:如果音乐媒体应用目前正持有音频焦点,而导航应用要请求获得焦点,那么通过该矩阵便能知道,这两个交互可以同时进行。
由于并发交互的缘故,可能会存在多个焦点持有者。在这种情况下,系统会将传入的焦点请求与当前的各个焦点持有者进行比较,然后决定应用哪种交互。此时,最保守的交互会胜出(先是拒绝交互,然后是独占交互,最后是并发交互)。
下表罗列了传入焦点请求的 CarAudioContext(列)与现有焦点持有者的上下文(行)之间的焦点交互。每个单元格表示两种上下文的预期交互类型,其中:
R 代表拒绝交互
E 代表独占交互
C 代表并发交互
duck指的是两个音频都在播放的时候,其中一个音频主动的降低音量。
AOSP legacy模式
关键代码MediaFocusControl.java、FocusRequester.java
AAOS 动态路由