Android Framework 音频子系统(15)音量调节之Setting界面调节音量流程

该系列文章总纲链接:专题分纲目录 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的音量。 

发布了291 篇原创文章 · 获赞 47 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/vviccc/article/details/105457922