该系列文章总纲链接:专题分纲目录 Android Framework 音频子系统
本章关键点总结 & 说明:
本章节主要关注➕ 以上思维导图左上 音量调节 中 流程分析中的 Setting界面调节音量流程部分 即可。主要说明了 滑动 音量滑动条 音量值是如何一层层传递的处理流程。
1 从notification_settings.xml找到程序入口
在Setting应用中,通过文件(packages/apps/Settings/res/xml/notification_settings.xml)定义音量滑动条:
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res/com.android.settings"
android:title="@string/notification_settings"
android:key="notification_settings"
settings:keywords="@string/keywords_sounds_and_notifications">
<PreferenceCategory
android:key="sound"
android:title="@string/sound_settings" >
<!-- Media volume -->
<com.android.settings.notification.VolumeSeekBarPreference
android:key="media_volume"
android:icon="@*android:drawable/ic_audio_vol"
android:title="@string/media_volume_option_title" />
<!-- Alarm volume -->
<com.android.settings.notification.VolumeSeekBarPreference
android:key="alarm_volume"
android:icon="@*android:drawable/ic_audio_alarm"
android:title="@string/alarm_volume_option_title" />
...
</PreferenceCategory>
</PreferenceScreen>
该文件定义了多个VolumeSeekBarPreference,这里的每个VolumeSeekBarPreference要跟一个SeekBar绑定,在VolumeSeekBarPreference的绑定函数onBindView中,设置了对应的SeekBar的SeekBarChangeListener (一个SeekBarVolumizer对象),代码如下所示:
@Override
protected void onBindView(View view) {
super.onBindView(view);
if (mStream == 0) {
Log.w(TAG, "No stream found, not binding volumizer");
return;
}
mSeekBar = (SeekBar) view.findViewById(com.android.internal.R.id.seekbar);
mIconView = (ImageView) view.findViewById(com.android.internal.R.id.icon);
mSuppressionTextView = (TextView) view.findViewById(R.id.suppression_text);
init();
}
2 从init函数分析到发消息 MSG_SET_DEVICE_VOLUME 给AudioService
这里最后调用了init函数,代码实现如下:
private void init() {
if (mSeekBar == null) return;
getPreferenceManager().registerOnActivityStopListener(this);
final SeekBarVolumizer.Callback sbvc = new SeekBarVolumizer.Callback() {
@Override
public void onSampleStarting(SeekBarVolumizer sbv) {
if (mCallback != null) {
mCallback.onSampleStarting(sbv);
}
}
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromTouch) {
if (mCallback != null) {
mCallback.onStreamValueChanged(mStream, progress);
}
}
@Override
public void onMuted(boolean muted) {
if (mMuted == muted) return;
mMuted = muted;
updateIconView();
}
};
final Uri sampleUri = mStream == AudioManager.STREAM_MUSIC ? getMediaVolumeUri() : null;
if (mVolumizer == null) {
//创建SeekBarVolumizer
mVolumizer = new SeekBarVolumizer(getContext(), mStream, sampleUri, sbvc);
}
mVolumizer.start();
//设置mSeekBar
mVolumizer.setSeekBar(mSeekBar);
updateIconView();
mCallback.onStreamValueChanged(mStream, mSeekBar.getProgress());
updateSuppressionText();
}
当SeekBar被滑动时, SeekBar对象的onProgressRefresh被调用,代码如下:
public class SeekBar extends AbsSeekBar {
//...
@Override
void onProgressRefresh(float scale, boolean fromUser) {
super.onProgressRefresh(scale, fromUser);
if (mOnSeekBarChangeListener != null) {
mOnSeekBarChangeListener.onProgressChanged(this, getProgress(), fromUser);
}
}
//...
}
该函数会调用 mOnSeekBarChangeListener.onProgressChanged去设置音量,这个mOnSeekBarChangeListener是在SeekBarVolumizer中的setSeekBarfan方法设置的,代码如下:
public void setSeekBar(SeekBar seekBar) {
if (mSeekBar != null) {
mSeekBar.setOnSeekBarChangeListener(null);
}
mSeekBar = seekBar;
mSeekBar.setOnSeekBarChangeListener(null);
mSeekBar.setMax(mMaxStreamVolume);
updateSeekBar();
mSeekBar.setOnSeekBarChangeListener(this);
}
这里的setOnSeekBarChangeListener,将在SeekBarVolumizer本身设置为SeekBar的mOnSeekBarChangeListener,所以这里关注 SeekBarVolumizer的onProgressChanged实现,代码如下:
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromTouch) {
if (fromTouch) {
postSetVolume(progress);
}
if (mCallback != null) {
mCallback.onProgressChanged(seekBar, progress, fromTouch);
}
}
这里继续分析postSetVolume,代码实现如下:
private void postSetVolume(int progress) {
if (mHandler == null) return;
// Do the volume changing separately to give responsive UI
mLastProgress = progress;
mHandler.removeMessages(MSG_SET_STREAM_VOLUME);
mHandler.sendMessage(mHandler.obtainMessage(MSG_SET_STREAM_VOLUME));
}
这里继续分析消息的处理流程,对应handler的handleMessage代码实现如下:
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
case MSG_SET_STREAM_VOLUME:
mAudioManager.setStreamVolume(mStreamType, mLastProgress,
AudioManager.FLAG_SHOW_UI_WARNINGS);
break;
//..
default:
Log.e(TAG, "invalid SeekBarVolumizer message: "+msg.what);
}
return true;
}
这里开始进入到音频子系统,mAudioManager的setStreamVolume代码实现如下:
public void setStreamVolume(int streamType, int index, int flags) {
IAudioService service = getService();
try {
if (mUseMasterVolume) {
service.setMasterVolume(index, flags, mContext.getOpPackageName());
} else {
service.setStreamVolume(streamType, index, flags, mContext.getOpPackageName());
}
} catch (RemoteException e) {
Log.e(TAG, "Dead object in setStreamVolume", e);
}
}
这里调用到AUdioService中的setStreamVolume,代码实现如下:
public void setStreamVolume(int streamType, int index, int flags, String callingPackage) {
setStreamVolume(streamType, index, flags, callingPackage, Binder.getCallingUid());
}
继续分析setStreamVolume,代码实现如下:
private void setStreamVolume(int streamType, int index, int flags, String callingPackage,
int uid) {
//...
synchronized (mSafeMediaVolumeState) {
// reset any pending volume command
//...
if (!checkSafeMediaVolume(streamTypeAlias, index, device)) {
mVolumeController.postDisplaySafeVolumeWarning(flags);
mPendingVolumeCommand = new StreamVolumeCommand(
streamType, index, flags, device);
} else {
//checkSafeMediaVolume为真时
onSetStreamVolume(streamType, index, flags, device);
index = mStreamStates[streamType].getIndex(device);
}
}
sendVolumeUpdate(streamType, oldIndex, index, flags);
}
这里继续分析onSetStreamVolume,代码实现如下:
private void onSetStreamVolume(int streamType, int index, int flags, int device) {
setStreamVolumeInt(mStreamVolumeAlias[streamType], index, device, false);
//...
}
继续分析setStreamVolumeInt,代码实现如下:
private void setStreamVolumeInt(int streamType,
int index,
int device,
boolean force) {
VolumeStreamState streamState = mStreamStates[streamType];
if (streamState.setIndex(index, device) || force) {
//最终都会调用AudioService.java的代码发出MSG
sendMsg(mAudioHandler,
MSG_SET_DEVICE_VOLUME,
SENDMSG_QUEUE,
device,
0,
streamState,
0);
}
}
最终发送了MSG_SET_DEVICE_VOLUME给mAudioHandler去处理,处理流程在Android Framework 音频子系统(14)音量调节之音量键处理流程 第3部分分析过。
说明:每一个SeekBar对应一个stream,滑动SeekBar时会设置该stream的音量,也会去设置同属一个alias(同一分组)的其他stream的音量。