前言
上一篇我们主要分析了关于CarAudioZone的CarVolumeGroup,今天我们继续看看剩下CarZonesAudioFocus
正文
首先还是看没有分析完setupDynamicRouting(SparseArray busToCarAudioDeviceInfo)的这个函数剩余部分
// Setup dynamic routing rules by usage
final CarAudioDynamicRouting dynamicRouting = new CarAudioDynamicRouting(mCarAudioZones);
dynamicRouting.setupAudioDynamicRouting(builder);
// Attach the {@link AudioPolicyVolumeCallback}
builder.setAudioPolicyVolumeCallback(mAudioPolicyVolumeCallback);
这部分后续说关于AUDIO_DEVICE_OUT_BUS的时候再说,主要做一个路由策略的处理。这里先不说了,继续看
if (sUseCarAudioFocus) {
// Configure our AudioPolicy to handle focus events.
// This gives us the ability to decide which audio focus requests to accept and bypasses
// the framework ducking logic.
mFocusHandler = new CarZonesAudioFocus(mAudioManager,
mContext.getPackageManager(),
mCarAudioZones);
builder.setAudioPolicyFocusListener(mFocusHandler);
builder.setIsAudioFocusPolicy(true);
}
mAudioPolicy = builder.build();
if (sUseCarAudioFocus) {
// Connect the AudioPolicy and the focus listener
mFocusHandler.setOwningPolicy(this, mAudioPolicy);
}
sUseCarAudioFocus默认是true,如果我们不想在car上使用CarZonesAudioFocus这套逻辑,可以改变这个值为false,目前的版本未提供修改的方法,建议通过配置文件的方式修改,如果为false,则AudioFocus的处理就会使用原生的逻辑了。我们看下这段代码,首先new CarZonesAudioFocus
CarZonesAudioFocus(AudioManager audioManager,
PackageManager packageManager,
@NonNull CarAudioZone[] carAudioZones) {
//Create the zones here, the policy will be set setOwningPolicy,
// which is called right after this constructor.
//检查有效性的工具类
Preconditions.checkNotNull(carAudioZones);
Preconditions.checkArgument(carAudioZones.length != 0,
"There must be a minimum of one audio zone");
//Create focus for all the zones
for (CarAudioZone audioZone : carAudioZones) {
Log.d(CarLog.TAG_AUDIO,
"CarZonesAudioFocus adding new zone " + audioZone.getId());
CarAudioFocus zoneFocusListener = new CarAudioFocus(audioManager, packageManager);
mFocusZones.put(audioZone.getId(), zoneFocusListener);
}
}
这里的逻辑主要是围绕carAudioZones处理的,上篇我们分析了carAudioZones是如何构建的,以及它的size是如何拿到的。其实这里根据carAudioZones为每个CarAudioZone创建一个 CarAudioFocus,那么我们也顺便看下CarAudioFocus
CarAudioFocus(AudioManager audioManager, PackageManager packageManager) {
mAudioManager = audioManager;
mPackageManager = packageManager;
}
没啥逻辑,回到CarZonesAudioFocus中,最后将CarAudioFocus存入mFocusZones的map中。到此new CarZonesAudioFocus就结束了。我们继续看下setupDynamicRouting中的
builder.setAudioPolicyFocusListener(mFocusHandler);
builder.setIsAudioFocusPolicy(true);
builder就是AudioPolicy的builder,在一进入setupDynamicRouting这个方法的时候就new了,这里将我们new的CarZonesAudioFocus作为参数通过setAudioPolicyFocusListener设置给了audiopolicy。那也就是说明了CarZonesAudioFocus应该是继承了setAudioPolicyFocusListener这里的参数,或者就是同一参数,我们再回到CarZonesAudioFocus的源码中看下
class CarZonesAudioFocus extends AudioPolicy.AudioPolicyFocusListener
果然是继承了AudioPolicy.AudioPolicyFocusListener的,那我们在看下 AudioPolicy.AudioPolicyFocusListener里都有哪些方法,
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) {}
}
这里重写了两个onAudioFocusRequest和onAudioFocusAbandon,这俩主要处理音源焦点用的,这里先不细说。接下来builder.setIsAudioFocusPolicy(true);以及把audiopolicy传给 CarZonesAudioFocus即mFocusHandler.setOwningPolicy(this, mAudioPolicy)这样初始化就结束了,我们简单总结一下CarZonesAudioFocus:首先在CarAudioService的初始化过程中会加载setupDynamicRouting,在setupDynamicRouting中会加载CarZonesAudioFocus,在CarZonesAudioFocus会根据传入的carAudioZones(上一篇分析过了主要管理音量,继xml解析有两个carAudioZone)创建对应个数的CarAudioFocus,目的就是把不同zone的音源分开处理。最后又将CarZonesAudioFocus注册给了AudioPolicy同时设置了setIsAudioFocusPolicy(true)(这个设置很重要,后面讲) 到此基本就结束了。我们最后再看下CarAudioFocus,有个很重要的方法,先看一个二位数组
private static int sInteractionMatrix[][] = {
// Row selected by playing sound (labels along the right)
// Column selected by incoming request (labels along the top)
// Cell value is one of INTERACTION_REJECT, INTERACTION_EXCLUSIVE, INTERACTION_CONCURRENT
// Invalid, Music, Nav, Voice, Ring, Call, Alarm, Notification, System
{ 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Invalid
{ 0, 1, 2, 1, 1, 1, 1, 2, 2 }, // Music
{ 0, 2, 2, 1, 2, 1, 2, 2, 2 }, // Nav
{ 0, 2, 0, 2, 1, 1, 0, 0, 0 }, // Voice
{ 0, 0, 2, 2, 2, 2, 0, 0, 2 }, // Ring
{ 0, 0, 2, 0, 2, 2, 2, 2, 0 }, // Context
{ 0, 2, 2, 1, 1, 1, 2, 2, 2 }, // Alarm
{ 0, 2, 2, 1, 1, 1, 2, 2, 2 }, // Notification
{ 0, 2, 2, 1, 1, 1, 2, 2, 2 }, // System
};
这个二维数组很有意思,一个行和列个数完全相同的矩阵。我们简单看下注释说明,行表示当前播放的context,列表述request的context,值表示3种意思 0、1、2分别代表
static final int INTERACTION_REJECT = 0; // Focus not granted
static final int INTERACTION_EXCLUSIVE = 1; // Focus granted, others loose focus
static final int INTERACTION_CONCURRENT = 2; // Focus granted, others keep focus
这个注释连我这英语四级达不到的人来说都看懂了,我就不再翻译了。我举个简单例子,如果现在播放music,这个时候申请一个电话的音频焦点,是个什么结果呢,我们先找到music所在的行,是sInteractionMatrix[1][]在找下电话的列即sInteractionMatrix[1][3],结果是1即Focus granted, others loose focus,也就是电话出声,音乐暂停,再找一个navi和music的是sInteractionMatrix[2][1],对应的value是2,即navi和music混音。与我们日常使用场景一样。突然觉得这个东西好高大上,比起Android原生的也就是FocusRequester中的两个switch的判断优先级的结果高级了太多了。
总结
通过这两篇文章,我觉得完成理解起来CarAudioZone还是有些困难的,我们后续会从一个具体的例子,以及具体的使用场景,来说明下个CarAudioZone是如何运行在Android的系统中的,以及又扮演着一个什么样的角色。
最后如果哪里说错啦,欢迎大家一起沟通交流~