android5.0设置模块音量调节流程
来源:互联网 发布:网络店铺铺招商广告语 编辑:程序博客网 时间:2024/04/30 09:35
最近刚好修改到相关方面的BUG,在这里整理总结一下。
设置中音量相关的Fragment调整到了NotificationSettings.java中。
我们可以在R.xml.notification_settings中看到如下
<!-- Media volume --> <com.android.settings.notification.VolumeSeekBarPreference android:key="media_volume" android:icon="@drawable/ic_audio_vol_24dp" android:title="@string/media_volume_option_title" /> <!-- Alarm volume --> <com.android.settings.notification.VolumeSeekBarPreference android:key="alarm_volume" android:icon="@drawable/ic_audio_alarm_24dp" android:title="@string/alarm_volume_option_title" /> <!-- Ring volume --> <com.android.settings.notification.VolumeSeekBarPreference android:key="ring_volume" android:icon="@drawable/ring_notif" android:title="@string/ring_volume_option_title" /> <!-- Notification volume --> <com.android.settings.notification.VolumeSeekBarPreference android:key="notification_volume" android:icon="@drawable/ring_notif" android:title="@string/notification_volume_option_title" />
这分别代表多媒体、闹钟、来电铃声和系统通知音量。所以在UI层面上用户是通过调节VolumeSeekBarPreference这个seekbar来最终对系统音量进行调整。
回头看NotificationSettings.java中初始化流程
//sound是包含上面4个SeekBar的PreferenceCategory final PreferenceCategory sound = (PreferenceCategory) findPreference(KEY_SOUND); initVolumePreference(KEY_MEDIA_VOLUME, AudioManager.STREAM_MUSIC); initVolumePreference(KEY_ALARM_VOLUME, AudioManager.STREAM_ALARM); //判断是否支持通话,支持移除NOTIFICATION_VOLUME否则移除RING_VOLUME if (mVoiceCapable) { mRingOrNotificationPreference = initVolumePreference(KEY_RING_VOLUME, AudioManager.STREAM_RING); sound.removePreference(sound.findPreference(KEY_NOTIFICATION_VOLUME)); } else { mRingOrNotificationPreference = initVolumePreference(KEY_NOTIFICATION_VOLUME, AudioManager.STREAM_NOTIFICATION); sound.removePreference(sound.findPreference(KEY_RING_VOLUME)); } initRingtones(sound); private final VolumePreferenceCallback mVolumeCallback = new VolumePreferenceCallback(); private VolumeSeekBarPreference initVolumePreference(String key, int stream) { final VolumeSeekBarPreference volumePref = (VolumeSeekBarPreference) findPreference(key); 设置callback volumePref.setCallback(mVolumeCallback); //设置AudioManager的stream,可以在AudioSystem中看到具体int值代表的意义 volumePref.setStream(stream); return volumePref; }
mVolumeCallback如下,当你拖动seekbar对音量进行调节时这个callback会回馈响应,在界面上进行相应的调整。
private final class VolumePreferenceCallback implements VolumeSeekBarPreference.Callback { //SeekBarVolumizer是主要负责对系统音量进行调整的类,会在后面描述 private SeekBarVolumizer mCurrent; @Override public void onSampleStarting(SeekBarVolumizer sbv) { if (mCurrent != null && mCurrent != sbv) { mCurrent.stopSample(); } mCurrent = sbv; if (mCurrent != null) { mHandler.removeMessages(H.STOP_SAMPLE); mHandler.sendEmptyMessageDelayed(H.STOP_SAMPLE, SAMPLE_CUTOFF); } } @Override public void onStreamValueChanged(int stream, int progress) { if (stream == AudioManager.STREAM_RING) { mHandler.removeMessages(H.UPDATE_RINGER_ICON); mHandler.obtainMessage(H.UPDATE_RINGER_ICON, progress, 0).sendToTarget(); } } public void stopSample() { if (mCurrent != null) { mCurrent.stopSample(); } } }; @Override public void handleMessage(Message msg) { switch (msg.what) { case UPDATE_PHONE_RINGTONE: mPhoneRingtonePreference.setSummary((CharSequence) msg.obj); break; case UPDATE_NOTIFICATION_RINGTONE: mNotificationRingtonePreference.setSummary((CharSequence) msg.obj); break; case STOP_SAMPLE: mVolumeCallback.stopSample(); break; case UPDATE_RINGER_ICON: updateRingOrNotificationIcon(msg.arg1); break; } }
下面来看VolumeSeekBarPreference.java中可以看到如下代码
private SeekBarVolumizer mVolumizer; final Uri sampleUri = mStream == AudioManager.STREAM_MUSIC ? getMediaVolumeUri() : null; if (mVolumizer == null) { mVolumizer = new SeekBarVolumizer(getContext(), mStream, sampleUri, sbvc) { // we need to piggyback on SBV's SeekBar listener to update our icon @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromTouch) { super.onProgressChanged(seekBar, progress, fromTouch); //这里的mCallback就是VolumePreferenceCallback对象 mCallback.onStreamValueChanged(mStream, progress); } }; } mVolumizer.setSeekBar(mSeekBar); mIconView = (ImageView) view.findViewById(com.android.internal.R.id.icon); mCallback.onStreamValueChanged(mStream, mSeekBar.getProgress()); @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromTouch) { super.onProgressChanged(seekBar, progress, fromTouch); mCallback.onStreamValueChanged(mStream, progress); }
这里我们进行下梳理在NotificationSettings.java内initVolumePreference函数的volumePref.setCallback(mVolumeCallback);把VolumePreferenceCallback这个callbak设置到VolumeSeekBarPreference.java内的mCallback中
使得SeekBarVolumizer内的onProgressChanged进行回调时NotificationSettings.java的相应回调函数都响应。
现在看下frameworks/base/core/java/android/preference的SeekBarVolumizer.java
private final Context mContext; private final Handler mHandler; private final H mUiHandler = new H(); private final Callback mCallback; private final Uri mDefaultUri; private final AudioManager mAudioManager; private final int mStreamType; private final int mMaxStreamVolume; private final Receiver mReceiver = new Receiver(); private final Observer mVolumeObserver; private int mOriginalStreamVolume; private Ringtone mRingtone; private int mLastProgress = -1; private SeekBar mSeekBar; private int mVolumeBeforeMute = -1; private static final int MSG_SET_STREAM_VOLUME = 0; private static final int MSG_START_SAMPLE = 1; private static final int MSG_STOP_SAMPLE = 2; private static final int MSG_INIT_SAMPLE = 3; private static final int CHECK_RINGTONE_PLAYBACK_DELAY_MS = 1000; public SeekBarVolumizer(Context context, int streamType, Uri defaultUri, Callback callback) { mContext = context; mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); mStreamType = streamType; mMaxStreamVolume = mAudioManager.getStreamMaxVolume(mStreamType); HandlerThread thread = new HandlerThread(TAG + ".CallbackHandler"); thread.start(); mHandler = new Handler(thread.getLooper(), this); mCallback = callback; mOriginalStreamVolume = mAudioManager.getStreamVolume(mStreamType); mVolumeObserver = new Observer(mHandler); mContext.getContentResolver().registerContentObserver( System.getUriFor(System.VOLUME_SETTINGS[mStreamType]), false, mVolumeObserver); mReceiver.setListening(true); //mDefaultUri为调整seekbar时听到的调试音 if (defaultUri == null) { if (mStreamType == AudioManager.STREAM_RING) { defaultUri = Settings.System.DEFAULT_RINGTONE_URI; } else if (mStreamType == AudioManager.STREAM_NOTIFICATION) { defaultUri = Settings.System.DEFAULT_NOTIFICATION_URI; } else { defaultUri = Settings.System.DEFAULT_ALARM_ALERT_URI; } } mDefaultUri = defaultUri; mHandler.sendEmptyMessage(MSG_INIT_SAMPLE); } public void setSeekBar(SeekBar seekBar) { if (mSeekBar != null) { mSeekBar.setOnSeekBarChangeListener(null); } mSeekBar = seekBar; mSeekBar.setOnSeekBarChangeListener(null); mSeekBar.setMax(mMaxStreamVolume); mSeekBar.setProgress(mLastProgress > -1 ? mLastProgress : mOriginalStreamVolume); mSeekBar.setOnSeekBarChangeListener(this); } @Override public boolean handleMessage(Message msg) { switch (msg.what) { //真正调节系统音量的地方 case MSG_SET_STREAM_VOLUME: mAudioManager.setStreamVolume(mStreamType, mLastProgress, AudioManager.FLAG_SHOW_UI_WARNINGS); break; case MSG_START_SAMPLE: onStartSample(); break; case MSG_STOP_SAMPLE: onStopSample(); break; case MSG_INIT_SAMPLE: onInitSample(); break; default: Log.e(TAG, "invalid SeekBarVolumizer message: "+msg.what); } return true; } //初始化mRingtone,我们知道系统播放的铃声最后是通过mRingtone.play()来实现的 private void onInitSample() { mRingtone = RingtoneManager.getRingtone(mContext, mDefaultUri); if (mRingtone != null) { mRingtone.setStreamType(mStreamType); } } private void postStartSample() { mHandler.removeMessages(MSG_START_SAMPLE); mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_START_SAMPLE), isSamplePlaying() ? CHECK_RINGTONE_PLAYBACK_DELAY_MS : 0); } //开始播放调试音 private void onStartSample() { if (!isSamplePlaying()) { if (mCallback != null) { mCallback.onSampleStarting(this); } if (mRingtone != null) { try { mRingtone.play(); } catch (Throwable e) { Log.w(TAG, "Error playing ringtone, stream " + mStreamType, e); } } } } void postStopSample() { // remove pending delayed start messages mHandler.removeMessages(MSG_START_SAMPLE); mHandler.removeMessages(MSG_STOP_SAMPLE); mHandler.sendMessage(mHandler.obtainMessage(MSG_STOP_SAMPLE)); } //停止播放调试音 private void onStopSample() { if (mRingtone != null) { mRingtone.stop(); } } public void stop() { postStopSample(); mContext.getContentResolver().unregisterContentObserver(mVolumeObserver); mSeekBar.setOnSeekBarChangeListener(null); mReceiver.setListening(false); mHandler.getLooper().quitSafely(); } public void revertVolume() { mAudioManager.setStreamVolume(mStreamType, mOriginalStreamVolume, 0); } public void onProgressChanged(SeekBar seekBar, int progress, boolean fromTouch) { if (!fromTouch) { return; } postSetVolume(progress); } void postSetVolume(int progress) { // 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)); } public void onStartTrackingTouch(SeekBar seekBar) { } public void onStopTrackingTouch(SeekBar seekBar) { postStartSample(); } //判断是否正在播放铃声 public boolean isSamplePlaying() { return mRingtone != null && mRingtone.isPlaying(); } public void startSample() { postStartSample(); } public void stopSample() { postStopSample(); } public SeekBar getSeekBar() { return mSeekBar; } public void changeVolumeBy(int amount) { mSeekBar.incrementProgressBy(amount); postSetVolume(mSeekBar.getProgress()); postStartSample(); mVolumeBeforeMute = -1; } public void muteVolume() { if (mVolumeBeforeMute != -1) { mSeekBar.setProgress(mVolumeBeforeMute); postSetVolume(mVolumeBeforeMute); postStartSample(); mVolumeBeforeMute = -1; } else { mVolumeBeforeMute = mSeekBar.getProgress(); mSeekBar.setProgress(0); postStopSample(); postSetVolume(0); } } public void onSaveInstanceState(VolumeStore volumeStore) { if (mLastProgress >= 0) { volumeStore.volume = mLastProgress; volumeStore.originalVolume = mOriginalStreamVolume; } } public void onRestoreInstanceState(VolumeStore volumeStore) { if (volumeStore.volume != -1) { mOriginalStreamVolume = volumeStore.originalVolume; mLastProgress = volumeStore.volume; postSetVolume(mLastProgress); } } private final class H extends Handler { private static final int UPDATE_SLIDER = 1; @Override public void handleMessage(Message msg) { if (msg.what == UPDATE_SLIDER) { if (mSeekBar != null) { mSeekBar.setProgress(msg.arg1); mLastProgress = mSeekBar.getProgress(); } } } public void postUpdateSlider(int volume) { obtainMessage(UPDATE_SLIDER, volume, 0).sendToTarget(); } } private final class Observer extends ContentObserver { public Observer(Handler handler) { super(handler); } @Override public void onChange(boolean selfChange) { super.onChange(selfChange); if (mSeekBar != null && mAudioManager != null) { final int volume = mAudioManager.getStreamVolume(mStreamType); mUiHandler.postUpdateSlider(volume); } } }
这里忽视其他所有操作关注一下主要流程,我们知道这个类implements了OnSeekBarChangeListener,这个listener包含了当值发生变化响应的onProgressChanged和开始完成操作的onStartTrackingTouch和onStopTrackingTouch
所以当开始拖动seekbar时onStartTrackingTouch响应无操作,然后onProgressChanged发生变化postSetVolume(progress);从而调用最关键函数mAudioManager.setStreamVolume(mStreamType, mLastProgress,
AudioManager.FLAG_SHOW_UI_WARNINGS);实现音量调整,然后调用onStopTrackingTouch,通过handler调用onStartSample函数来播放调试音,在这过程中通过和setting模块绑定的
callback来对setting模块进行响应,这就时和上层setting模块互动的一个主要流程。
- android5.0设置模块音量调节流程
- android设置中拖动音量条调节音量流程(android5.1)
- android5.0 按键调节音量详解
- audiosystem 音量调节流程
- 005 音量上下键调节音量流程
- Android4.0 音量调节
- Android5.1 Audio计算音量流程
- Android 4.4 音量调节流程分析(一)
- Android 4.4 音量调节流程分析(二)
- Android 4.4 音量调节流程分析(一)
- Android 4.4 音量调节流程分析
- Android 4.4 音量调节流程分析(二)
- Android 4.4 音量调节流程分析(一)
- Andorid6.0音量调整(设置)流程
- 音量调节
- 音量调节
- 音量调节
- Android Audio音量设置流程
- 【RAC】 RAC For W2K8R2 安装 (一)
- 设计模式五 监听器模式(android) & 代理模式(ios)
- AndroidKK4.4 Power key电量屏幕时button light先亮LCD后亮的卡顿问题分析
- spring事务管理几种方式
- freemarker的数字不精确问题
- android5.0设置模块音量调节流程
- 词典查询
- linux PIE 程序
- 2012年5月SAT香港真题解析
- Hadoop全分布环境配置过程
- JS DOM操作
- extj.js学习笔记
- 第一篇博客
- 如何ScrollView在XIB或者Storyboard中设置约束并实现翻页滚动效果