Android音频焦点(一)

场景:如果我们播放音乐的时候,突然来了一条短信,这个时候,我们即希望能听清楚短信的提示语,不希望音乐停止而降低音量,之后在恢复音量。可以引入音频焦点(audio focus)这一特性,可以让我们方便管理音频焦点,从而让app可以应对各种情况。
音频焦点具有协调性,也就是说在同一时刻,只有一个app可以获取音频焦点。

音频焦点的处理流程:

1.在使用音频前(例如播放音乐),需要先获取焦点,如果成功获取到焦点,才应该正式开始工作。
2.当其他app请求了焦点,而此时我们的app失去焦点,我们需要做相应的处理。例如,如果是短暂型失去焦点,我们可以暂停工作,如果是永久性失去焦点,我们就停止工作。
3.当完成工作后,我们需要手动释放音频焦点。

8.0之前的实现
当你要录音或者播放一首歌曲的时候,最好先请求音频焦点,这个时候你需要调用AudioManager.requestAudioFocus(三个参数)方法
第一个参数用于监听焦点的变化,列如你的app在工作期间可能会失去焦点。
第二个参数表明请求的音频焦点影响的是哪种类型流,列如,如果我们录音,我们肯定要影响Music这一类型的音频流,因此可以选择AudioManager.STREAM_MUSIC。还有许多类型,列如与铃声有关的AudioManager.STREAM_RING,与通话有关的是AudioManager.STREAM_VOICE_CALL。
第三个参数用于表明音频焦点持续时间,也有许多类型。
1.AudioManager.AUDIOFOCUS_GAIN: API文档说请求的这类音频焦点持续时间是未知的,通常用来表示长时间获取焦点,可以用来播放音乐,录音等等。
2.AudioManager.AUDIOFOCUS_GAIN_TRANSIENT: 表明请求的音频焦点持续时间比较短,通常用来播放导航路线的声音,或者播放通知声音。
3.AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK: 这个也是表明请求的音频焦点持续时间比较短,但是在这段时间内,它希望其他应用以较低音量继续播放。例如,我们在使用导航的时候可以听音乐,当出现导航语音的时候,音乐音量会降低以便我们能听清楚导航的语音,当导航语音播放完毕后,音乐恢复音量,继续播放。
4.AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE: 这个也是表明音请求的音频焦点持续时间比较短,但是在这段时间内,不希望任何应用(包括系统应用)来做任何与音频相关的事情,就算是降低音量播放音乐也是不被希望的。例如当我们进行录音或者语音识别的时候,我们不希望其他的声音出现干扰。
AudioManager.requestAudioFocus()的返回值表明请求的结果,AudioManager.AUDIOFOCUS_REQUEST_FAILED表明请求焦点失败,AudioManager.AUDIOFOCUS_REQUEST_GRANTED表明请求焦点成功。
当我们成功请求焦点后,就可以做一些与音频有关的事情,例如播放音乐,录音,或者语音识别。当完成这些工作后,我们必须调用AudioManager.abandonAudioFocus(onAudioFocusChangeListener l)释放音频焦点。

关于请求焦点变化,只会返回两种结果,要么请求成功(AUDIOFOCUS_REQUEST_GRANTED),要么请求失败(AUDIOFOCUS_REQUEST_FAILED)。
从8.0(API 26)之后增加了一种结果,延迟成功请求(AUDIOFOCUS_REQUEST_DELAYED),这个也是成功的请求但是这个请求具有延迟性。列如当我们处于通话状态的时候,我们很显然不希望任何app来获取到音频焦点来做些事,列如播放音乐。
然而只有设置过AudioFocusRequest.Builder.setAcceptsDelayedFocusGain(true)才能获取到这种结果,这个我们后面会讲到。那么我们怎么知道什么时候获取到了音频焦点呢,当然还需要设置AudioManager.OnAudioFocusChangeListener这个音频焦点变化的监听器,通过回调确认何时获取到了音频焦点。

自动降低音量

在Android8.0之前,如果请求焦点使用了AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK参数,它表明希望拥有了音频焦点的其他应用降低音量来使用音频,然而并不是所有的应用都会这样做(也可能是开发者忘记优化),因为这并不是系统强制的。但是从Android8.0开始,这个降低音量的工作就是系统默认行为了,可以说是一个良心的优化。如果我不希望系统给我自动降低音量,而是想自己暂停音频相关的工作,那咋办?这个可以通过AudioFocusRequest.Builder.setWillPauseWhenDucked(true)方法取消系统的默认行为,然后通过监听音频焦点变化,来自己处理。

音频焦点请求方式

请求和释放音频焦点的方法名没有改变,都是用AudioManager.requestAudioFocus()请求焦点,用AudioManager.releaseAudioFocus()来释放焦点,但是这两个方法在Android8.0后需要传入一个AudioFocusRequest对象,这个对象的构造方式使用的是Builder模式设置参数,下面看一些设置参数的方法:
1:setFocusGain(): 只有这个方法是必须的,而传入的参数与8.0之前使用AudioManager.requestAudioFocus()传入的第三个参数一样,都是用来表示持续时间。
2:setAudioAttributes(): 这个方法是用来描述app的使用情况。这方法需要传入一个AudioAttributes对象,这个对象也是使用Builder模式来构造,例如使用AudioAttributes.Builder.setUsage()来描述使用这个音频来干什么,我们可以传入一个AudioAttributes.USAGE_MEDIA来表明用这个音频来作为媒体文件来播放,也可以传入一个AudioAttributes.USAGE_ALARM来表明用这个来作为闹铃。
3:setWillPauseWhenDucked(): 这个前面说过,是为了覆盖系统默认降低音量的行为,但是必须要设置AudioManager.OnAudioFocusChangeListener才能自己处理这类情况。
4:setAcceptsDelayedFocusGain():这个前面也说过,这个是为了能够延迟获取到焦点的必须条件,但是同时也必须要设置AudioManager.OnAudioFocusChangeListener才能得知何时获取到焦点。
5:setOnAudioFocusChangeListener(): 音频焦点变化监听器。值得一提的是这个方法有个重载的方法,有一个重载方法有两个参数,第二个参数为Handler对象,看到Handler应该明白了,是为了使用它的消息队列来顺序处理这个回调。

监听音频焦点的变化

在请求音频焦点的时候,我们需要监听焦点的变化,以便做出正确的行为,我们需要设置并实现OnAudioFocusChangeListener接口。
public interface OnAudioFocusChangeListener {
public void onAudioFocusChange(int focusChange);
}
根据参数int focusChange的值,我们需要做出合适的行为,而这些值大致可以分为两种类型,一种是短暂失去焦点,另一种是永久失去焦点。

短暂失去焦点

这种类型包括
AudioManager.AUDIOFOCUS_LOSS_TRANSIENT :我们应该暂停音乐的播放

AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK :我们应该降低音量继续播放,当然根据工作内容的不同,相应的处理也会有所不同。
如果我们还想恢复状态继续工作,那么在短暂失去音频焦点后,应该持续监听焦点的变化,当我们再次获取焦点的时候,就恢复正常的工作状态,例如调整音量播放,或者恢复播放暂停的音乐。就如同我们前面举个导航和音乐的例子,当有导航声音的时候,音乐需要降低音量播放,当导航声音播放完毕,音乐需要恢复音量。

永久失去焦点

这种类型是指 AudioManager.AUDIOFOCUS_LOSS,当遇到这种功能的情况,我们需要立即暂停,甚至有时候需要停止正在进行的工作,因为我们将不再回获取到焦点,那么为了恢复工作需要用户手动去点击播放。

代码实现

我们在项目中处理录音的时候,需要去请求焦点,于是我做了一个简单的封装,代码如下:
public class AudioFocusManager implements AudioManager.OnAudioFocusChangeListener {
private AudioManager mAudioManager;
private AudioFocusRequest mFocusRequest;
private AudioAttributes mAudioAttributes;

private onRequestFocusResultListener mOnRequestFocusResultListener;
private OnAudioFocusChangeListener mAudioFocusChangeListener;

public AudioFocusManager(Context context) {
    mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
}

/**
 * Request audio focus.
 */
public void requestFocus() {
    int result;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        if (mFocusRequest == null) {
            if (mAudioAttributes == null) {
                mAudioAttributes = new AudioAttributes.Builder()
                        .setUsage(AudioAttributes.USAGE_MEDIA)
                        .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
                        .build();
            }
            mFocusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
                    .setAudioAttributes(mAudioAttributes)
                    .setWillPauseWhenDucked(true)
                    .setOnAudioFocusChangeListener(this)
                    .build();
        }
        result = mAudioManager.requestAudioFocus(mFocusRequest);
    } else {
        result = mAudioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
    }
    if (mOnRequestFocusResultListener != null) {
        mOnRequestFocusResultListener.onHandleResult(result);
    }
}


@Override
public void onAudioFocusChange(int focusChange) {
    if (mAudioFocusChangeListener != null) {
        mAudioFocusChangeListener.onAudioFocusChange(focusChange);
    }
}

/**
 * Release audio focus.
 */
public void releaseAudioFocus() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        mAudioManager.abandonAudioFocusRequest(mFocusRequest);
    } else {
        mAudioManager.abandonAudioFocus(this);
    }
}

/**
 * Handle the result of audio focus.
 */
public interface onRequestFocusResultListener {
    void onHandleResult(int result);
}


public void setOnHandleResultListener(onRequestFocusResultListener listener) {
    mOnRequestFocusResultListener = listener;
}


/**
 * Same as AudioManager.OnAudioFocusChangeListener.
 */
public interface OnAudioFocusChangeListener {
    void onAudioFocusChange(int focusChange);
}


public void setOnAudioFocusChangeListener(OnAudioFocusChangeListener listener) {
    mAudioFocusChangeListener = listener;
}

}

你可能感兴趣的:(android,音频编码解码)