背景:
物理音量按键也可以调节声音,设置界面的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)
总结图如下:


被折叠的 条评论
为什么被折叠?



