前言
上一篇我們主要分析了關于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的源碼中看下
果然是繼承了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的系統中的,以及又扮演着一個什麼樣的角色。
最後如果哪裡說錯啦,歡迎大家一起溝通交流~