学习安卓audio疑问解答:物理按键调节声音设置界面的Seekbar为什么会跟着一起同步变化?

背景:

物理音量按键也可以调节声音,设置界面的Seekbar也可以调节声音,请问二者是如何进行同步的呢?
在这里插入图片描述
物理音量调节后设置的画面的也seekbar会同步动起来,本文来剖析一下为啥可以进行音量进度条的同步。

音量调节后AudioService发送相关的变化广播

VolumeStreamState类的setIndex中会进行对应的音量变化的广播

public boolean setIndex(int index, int device, String caller,
                boolean hasModifyAudioSettings) {
       
            if (changed) {
          
                if ((index != oldIndex) && isCurrentDevice) {
                    // for single volume devices, only send the volume change broadcast
                    // on the alias stream
                    if (!mIsSingleVolume || (mStreamVolumeAlias[mStreamType] == mStreamType)) {
                        //给广播的Intent设置当前音量index和老音量oldIndex
                        mVolumeChanged.putExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, index);
                        mVolumeChanged.putExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE,
                                oldIndex);
                        mVolumeChanged.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE_ALIAS,
                                mStreamVolumeAlias[mStreamType]);
//广播发送
                        sendBroadcastToAll(mVolumeChanged, mVolumeChangedOptions);
                    }
                }
            }
            return changed;
        }

Setting端的同步更新部分剖析

广播方式通知数据更新:
音量调节后会广播发送由AudioService发送
frameworks/base/core/java/android/preference/SeekBarVolumizer.java

 @Override
        public void onReceive(Context context, Intent intent) {
            final String action = intent.getAction();
            if (AudioManager.VOLUME_CHANGED_ACTION.equals(action)) {
            //获取广播intent中的相关的streamType和streamValue
                int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
                int streamValue = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, -1);
                if (mDeviceHasProductStrategies && !isDelay()) {
                //调用updateVolumeSlider进行相关的ui上更新
                    updateVolumeSlider(streamType, streamValue);
                }
            } 

对应的堆栈:

postUpdateSlider:582, SeekBarVolumizer$H (android.preference)
updateVolumeSlider:684, SeekBarVolumizer$Receiver (android.preference)
onReceive:633, SeekBarVolumizer$Receiver (android.preference)
lambda$getRunnable$0:1814, LoadedApk$ReceiverDispatcher$Args (android.app)
run:0, LoadedApk$ReceiverDispatcher$Args$$ExternalSyntheticLambda0 (android.app)
handleCallback:959, Handler (android.os)
dispatchMessage:100, Handler (android.os)
loopOnce:232, Looper (android.os)
loop:317, Looper (android.os)
main:8705, ActivityThread (android.app)
invoke:-1, Method (java.lang.reflect)
run:580, RuntimeInit$MethodAndArgsCaller (com.android.internal.os)
main:886, ZygoteInit (com.android.internal.os)

音量变化广播是否属于唯一通知方式呢?

为了验证这个问题可以考虑把AudioService端的广播发送部分代码屏蔽了,然后看看设置中的音量进度条是否还会同步,按预想广播通知音量数据变化,现在广播都不发送的肯定是不会了,那真的是这样么?

非广播数据导致的的回调更新UI剖析:

经过调试发现还是会调用到

public void postUpdateSlider(int volume, int lastAudibleVolume, boolean mute) {
            obtainMessage(UPDATE_SLIDER, volume, lastAudibleVolume, Boolean.valueOf(mute))
                    .sendToTarget();
        }

这里经过追踪发现是updateSlider触发的调用

    private void updateSlider() {
        if (mSeekBar != null && mAudioManager != null) {
            final int volume = mAudioManager.getStreamVolume(mStreamType);
            final int lastAudibleVolume = mAudioManager.getLastAudibleStreamVolume(mStreamType);
            final boolean mute = mAudioManager.isStreamMute(mStreamType);
            mUiHandler.postUpdateSlider(volume, lastAudibleVolume, mute);
        }
    }

在经过最终发现是由VolumeHandler的MSG_GROUP_VOLUME_CHANGED消息进行调用的

    private class VolumeHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            SomeArgs args = (SomeArgs) msg.obj;
            switch (msg.what) {
                case MSG_GROUP_VOLUME_CHANGED:
             //这里会调用上面的updateSlider触发进度条变化
                    updateSlider();
                    break;
            }
        }
    }

那这里的VolumeHandler的MSG_GROUP_VOLUME_CHANGED是如何发送的呢?

相关的发送代码追踪后如下:

frameworks/base/core/java/android/preference/SeekBarVolumizer.java

  private void registerVolumeGroupCb() {
        if (mVolumeGroupId != AudioVolumeGroup.DEFAULT_VOLUME_GROUP) {
            mAudioManager.registerVolumeGroupCallback(Runnable::run, mVolumeGroupCallback);
            updateSlider();
        }
    }


    private final AudioManager.VolumeGroupCallback mVolumeGroupCallback =
            new AudioManager.VolumeGroupCallback() {
        @Override
        public void onAudioVolumeGroupChanged(int group, int flags) {
            if (mHandler == null) {
                return;
            }
            SomeArgs args = SomeArgs.obtain();
            args.arg1 = group;
            args.arg2 = flags;
            
//可以看到这里会进行发送MSG_GROUP_VOLUME_CHANGED消息从而触发UI的更新
            mVolumeHandler.sendMessage(mHandler.obtainMessage(MSG_GROUP_VOLUME_CHANGED, args));
        }
    };

那么问题又来了,这里的mVolumeGroupCallback又是哪里触发的onAudioVolumeGroupChanged回调呢?这里就可能设计比较多了

VolumeGroupCallback部分剖析

frameworks/base/media/java/android/media/AudioManager.java

   /**
    * @hide
    * Register an audio volume group change listener.
    * @param callback the {@link VolumeGroupCallback} to register
    */
    @SystemApi
    public void registerVolumeGroupCallback(
            @NonNull Executor executor,
            @NonNull VolumeGroupCallback callback) {
    
        sAudioAudioVolumeGroupChangedHandler.init();
        // TODO: make use of executor
        sAudioAudioVolumeGroupChangedHandler.registerListener(callback);
    }

cb被添加到了mListeners这个集合中

 /**
    * @param cb the {@link AudioManager.VolumeGroupCallback} to register
    */
    public void registerListener(@NonNull AudioManager.VolumeGroupCallback cb) {
        Preconditions.checkNotNull(cb, "volume group callback shall not be null");
        synchronized (this) {
            mListeners.add(cb);
        }
        if (mHandler != null) {
            Message m = mHandler.obtainMessage(
                    AUDIOVOLUMEGROUP_EVENT_NEW_LISTENER, 0, 0, cb);
            mHandler.sendMessage(m);
        }
    }

等到后续native层面会就行回调

    @SuppressWarnings("unused")
    private static void postEventFromNative(Object moduleRef,
                                            int what, int arg1, int arg2, Object obj) {
        AudioVolumeGroupChangeHandler eventHandler =
                (AudioVolumeGroupChangeHandler) ((WeakReference) moduleRef).get();
        if (eventHandler == null) {
            return;
        }

        if (eventHandler != null) {
            Handler handler = eventHandler.handler();
            if (handler != null) {
                Message m = handler.obtainMessage(what, arg1, arg2, obj);
                // Do not remove previous messages, as we would lose notification of group changes
                handler.sendMessage(m);
            }
        }
    }

在init方法中会对这个消息进行处理
frameworks/base/media/java/android/media/audiopolicy/AudioVolumeGroupChangeHandler.java

    public void init() {
  
            mHandler = new Handler(mHandlerThread.getLooper()) {
                @Override
                public void handleMessage(Message msg) {
                    ArrayList<AudioManager.VolumeGroupCallback> listeners;
                    synchronized (this) {
                        if (msg.what == AUDIOVOLUMEGROUP_EVENT_NEW_LISTENER) {
                            listeners =
                                    new ArrayList<AudioManager.VolumeGroupCallback>();
                                    //可以看到这里被添加到了mListeners集合中
                            if (mListeners.contains(msg.obj)) {
                                listeners.add(
                                        (AudioManager.VolumeGroupCallback) msg.obj);
                            }
                        } else {
                            listeners = (ArrayList<AudioManager.VolumeGroupCallback>)
                                    mListeners.clone();
                        }
                    }
               

                    switch (msg.what) {
                        case AUDIOVOLUMEGROUP_EVENT_VOLUME_CHANGED:
                            for (int i = 0; i < listeners.size(); i++) {
                            //这里会回调到最开始的SeekBarVolumizer的注册的cb
                                listeners.get(i).onAudioVolumeGroupChanged((int) msg.arg1,
                                                                           (int) msg.arg2);
        }
    }

native层面触发的postEventFromNative剖析

这块经过追踪发现调用流程如下:
frameworks/base/core/jni/android_media_AudioVolumeGroupCallback.cpp

void JNIAudioVolumeGroupCallback::onAudioVolumeGroupChanged(volume_group_t group, int flags)
{
    JNIEnv *env = AndroidRuntime::getJNIEnv();
    if (env == NULL) {
        return;
    }
    ALOGV("%s volume group id %d", __FUNCTION__, group);
    env->CallStaticVoidMethod(mClass,
                              gAudioVolumeGroupChangeHandlerMethods.postEventFromNative,
                              mObject,
                              AUDIOVOLUMEGROUP_EVENT_VOLUME_CHANGED, group, flags, NULL);
    if (env->ExceptionCheck()) {
        ALOGW("An exception occurred while notifying an event.");
        env->ExceptionClear();
    }
}

那么接下来就是要分析native端如何调用到这里JNIAudioVolumeGroupCallback::onAudioVolumeGroupChanged方法的。这块比较复杂了就不过多展开,主要都是一些类的包装继承调用,核心就是AudioPolicy端跨进程到app的AudioSystem。

JNIAudioVolumeGroupCallback::onAudioVolumeGroupChanged这个调用实际上是远端AudioPolicyService调用到Client的,调用的堆栈如下:

09-22 21:47:43.486   373   475 D onAudioVolumeGroupChanged:   #00 pc 00000000001f3332  /system/bin/audioserver (android::AudioPolicyService::NotificationClient::onAudioVolumeGroupChanged(android::volume_group_t, int)+98) (BuildId: 7dd9e2e68a333b6dfcd47150897396da)
09-22 21:47:43.486   373   475 D onAudioVolumeGroupChanged:   #01 pc 00000000001efedc  /system/bin/audioserver (android::AudioPolicyService::doOnAudioVolumeGroupChanged(android::volume_group_t, int)+188) (BuildId: 7dd9e2e68a333b6dfcd47150897396da)
09-22 21:47:43.486   373   475 D onAudioVolumeGroupChanged:   #02 pc 00000000001edd30  /system/bin/audioserver (android::AudioPolicyService::AudioCommandThread::threadLoop()+3088) (BuildId: 7dd9e2e68a333b6dfcd47150897396da)
09-22 21:47:43.486   373   475 D onAudioVolumeGroupChanged:   #03 pc 0000000000011894  /system/lib64/libutils.so (android::Thread::_threadLoop(void*)+324) (BuildId: 2ccdabdfae4ce3f8fa2cc828ba6cf1b1)

明显看出这里其实是在AudioCommandThread出发的,那么是哪里往AudioCommandThread线程中抛入了相关消息呢?

changeAudioVolumeGroupCommand是靠setStreamVolumeIndex这个跨进程接口调用过来的

9-22 21:47:43.484   373   645 D changeAudioVolumeGroupCommand:   #00 pc 00000000001f716b  /system/bin/audioserver (android::AudioPolicyService::AudioCommandThread::changeAudioVolumeGroupCommand(android::volume_group_t, int)+491) (BuildId: 7dd9e2e68a333b6dfcd47150897396da)
09-22 21:47:43.484   373   645 D changeAudioVolumeGroupCommand:   #01 pc 000000000006c676  /system/lib64/libaudiopolicymanagerdefault.so (android::AudioPolicyManager::setVolumeIndexForAttributes(audio_attributes_t const&, int, audio_devices_t)+5974) (BuildId: bb7c4213a8c34f2f80dd11f810a5811b)
09-22 21:47:43.484   373   645 D changeAudioVolumeGroupCommand:   #02 pc 000000000006aa49  /system/lib64/libaudiopolicymanagerdefault.so (android::AudioPolicyManager::setStreamVolumeIndex(audio_stream_type_t, int, audio_devices_t)+345) (BuildId: bb7c4213a8c34f2f80dd11f810a5811b)
09-22 21:47:43.484   373   645 D changeAudioVolumeGroupCommand:   #03 pc 0000000000205c8e  /system/bin/audioserver (android::AudioPolicyService::setStreamVolumeIndex(android::media::audio::common::AudioStreamType, android::media::audio::common::AudioDeviceDescription const&, int)+270) (BuildId: 7dd9e2e68a333b6dfcd47150897396da)
09-22 21:47:43.484   373   645 D changeAudioVolumeGroupCommand:   #04 pc 000000000003e5ae  /system/lib64/audiopolicy-aidl-cpp.so (android::media::BnAudioPolicyService::onTransact(unsigned int, android::Parcel const&, android::Parcel*, unsigned int)+20318) (BuildId: 7f9f962dc3c3a9699e71deeb561a20d9)
09-22 21:47:43.484   373   645 D changeAudioVolumeGroupCommand:   #05 pc 00000000001e25e1  /system/bin/audioserver (android::AudioPolicyService::onTransact(unsigned int, android::Parcel const&, android::Parcel*, unsigned int)+1601) (BuildId: 7dd9e2e68a333b6dfcd47150897396da)
09-22 21:47:43.484   373   645 D changeAudioVolumeGroupCommand:   #06 pc 0000000000060fe1  /system/lib64/libbinder.so (android::BBinder::transact(unsigned int, android::Parcel const&, android::Parcel*, unsigned int)+241) (BuildId: 3fb041052325e616d19775d7a2a0cda4)
09-22 21:47:43.484   373   645 D changeAudioVolumeGroupCommand:   #07 pc 000000000004e0f3  /system/lib64/libbinder.so (android::IPCThreadState::executeCommand(int)+1251) (BuildId: 3fb041052325e616d19775d7a2a0cda4)
09-22 21:47:43.484   373   645 D changeAudioVolumeGroupCommand:   #08 pc 000000000004db4c  /system/lib64/libbinder.so (android::IPCThreadState::getAndExecuteCommand()+156) (BuildId: 3fb041052325e616d19775d7a2a0cda4)
09-22 21:47:43.484   373   645 D changeAudioVolumeGroupCommand:   #09 pc 000000000004e62f  /system/lib64/libbinder.so (android::IPCThreadState::joinThreadPool(bool)+111) (BuildId: 3fb041052325e616d19775d7a2a0cda4)
09-22 21:47:43.484   373   645 D changeAudioVolumeGroupCommand:   #10 pc 0000000000056ad7  /system/lib64/libbinder.so (android::PoolThread::threadLoop()+23) (BuildId: 3fb041052325e616d19775d7a2a0cda4)
09-22 21:47:43.484   373   645 D changeAudioVolumeGroupCommand:   #11 pc 0000000000011894  /system/lib64/libutils.so (android::Thread::_threadLoop(void*)+324) (BuildId: 2ccdabdfae4ce3f8fa2cc828ba6cf1b1)

总结图如下:

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

千里马学框架

帮助你了,就请我喝杯咖啡

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值