天天看點

Android10.0CarAudioZone(二)

前言

上一篇我們主要分析了關于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的系統中的,以及又扮演着一個什麼樣的角色。

最後如果哪裡說錯啦,歡迎大家一起溝通交流~