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模块互动的一个主要流程。

0 0
原创粉丝点击