《Android音频焦点机制深入理解》

Handling audio focus

Even though only one activity can run at any given time, Android is amulti-tasking environment. This poses a particular challenge to applications that use audio, because there is only one audio output and there may be several media services competing for its use. Before Android 2.2, there was no built-in mechanism to address this issue, which could in some cases lead to a bad user experience. For example, when a user is listening to music and another application needs to notify the user of something very important,the user might not hear the notification tone due to the loud music. Starting withAndroid 2.2, the platform offers a way for applications to negotiate their use of the device's audio output. This mechanism is called Audio Focus.

虽然在任意一个给定时间,只有一个activity能运行,android也是一个多任务环境。这给应用程序使用音频带来了一个特殊的挑战,因为只有一个音频输出,并且可能有几个媒体服务争夺使用这个音频输出。在Android 2.2之前,没有内建的机制来解决这个问题,这个问题在一些case下能导致一个坏的用户体验。例如,在用户正在听音乐时,其他应用需要通知用户非常重要的事情,用户可能不会听到通知铃声,因为声音很大的音乐。从Android 2.2 开始,平台提供一个方法为应用,为了让这些播放音频的应用协调他们如何使用设备的音频输出。这个机制被叫做音频焦点。


When your application needs to output audio such as music or a notification,you should always request audio focus. Once it has focus, it can use the sound output freely, but it should always listen for focus changes. If it is notified that it has lost the audiofocus, it should immediately either kill the audio or lower it to a quiet level(known as "ducking"—there is a flag that indicates which one is appropriate) and only resumeloud playback after it receives focus again.


当你的应用需要输出像是音乐或者通知这样的音频,你总是应该请求音频焦点。一旦你的app拥有焦点,它就能自由的使用声音输出,但是它应该总是监听焦点的改变。如果它被通知它已经失去了音频焦点,它就应该立即杀掉音频或者降低它的音量到静音的水平(被称为“ducking”- 有一个标记,用来表明哪一个是适合的),并且只能在它接收到焦点再次获取时恢复回放。


Audio Focus is cooperative in nature. That is, applications are expected(and highly encouraged) to comply with the audio focus guidelines, but the rules are not enforced by the system. If an application wants to play loud music even after losing audio focus, nothing in the system will prevent that.However, the user is more likely to have a bad experience and will be more likely to uninstall the misbehaving application.


音频焦点本质上是合作机制。这就是,应用被期望遵守音频焦点指导方针,但是这个规则不是被系统强力执行的。如果一个应用想要继续大声播放音乐,甚至在失去音频焦点后,系统中不会有任何东西来阻止它。然而,用户更可能有一个坏的体验,并且将会更可能卸载掉这样行为不端的应用。


To request audio focus, you must call requestAudioFocus() from the AudioManager, as the example below demonstrates:

为了请求音频焦点,你必须从AudioManager调用 requestAudioFocus() 函数 , 像下面示范的例子:

AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
int result = audioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC,
    AudioManager.AUDIOFOCUS_GAIN);

if (result != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
    // could not get audio focus.
}

The first parameter to requestAudioFocus()is an AudioManager.OnAudioFocusChangeListener,whose onAudioFocusChange() method is called whenever there is a change in audio focus. Therefore, you should also implement this interface on your service and activities. For example:

requestAudioFocus() 函数的第一个参数时一个监听:AudioManager.OnAudioFocusChangeListener, 这个监听的onAudioFocusChange()方法会被调用,在音频焦点有改变的时候。因此,你也应该实现这个接口,在你的service和activity里。例如:

class MyService extends Service
                implements AudioManager.OnAudioFocusChangeListener {
    // ....
    public void onAudioFocusChange(int focusChange) {
        // Do something based on focus change...
    }
}

The focusChange parameter tells you how the audio focus has changed, and can be one of the following values (they are all constants defined in AudioManager):

focusChange 参数告诉你音频焦点有怎样的改变,这个改变能是以下值中的一个(它们所有都被定义在 AudioManager里);

  • AUDIOFOCUS_GAIN: You have gained the audio focus.

            音频焦点获取:你已经获取到音频焦点。

  • AUDIOFOCUS_LOSS: You have lost the audio focus for a presumably long time.You must stop all audio playback. Because you should expect not to have focus back for a long time, this would be a good place to clean up your resources as much as possible. For example, you should release the MediaPlayer.

            音频焦点失去:你已经可能在很长时间里失去音频焦点。你必须停止所有音频回放。因为你应该预料到在很长时间里不会有焦点回来 , 这应该是一个尽可能清理你的   资源的好地方。例如,你应该释放MediaPlayer。

  • AUDIOFOCUS_LOSS_TRANSIENT: You have temporarily lost audio focus, but should receive it back shortly. You must stop all audio playback, but you can keep your resources because you will probably get focus back shortly.

            音频焦点瞬时失去:你已经临时失去音频焦点,但是很快应该会接收到焦点返回。你必须停止所有音频回放,但是你能保持你的资源不变,因为你将可能很快再次获得焦点。

  • AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: You have temporarily lost audio focus,but you are allowed to continue to play audio quietly (at a low volume) instead of killing audio completely.

            音频焦点瞬时失去带闪避:你已经临时的失去音频焦点,但是你被允许继续小声的播放音频,代替完全的杀掉音频播放。

Here is an example implementation:

这里是一个例子实现:

public void onAudioFocusChange(int focusChange) {
    switch (focusChange) {
        case AudioManager.AUDIOFOCUS_GAIN:
            // resume playback
            if (mMediaPlayer == null) initMediaPlayer();
            else if (!mMediaPlayer.isPlaying()) mMediaPlayer.start();
            mMediaPlayer.setVolume(1.0f, 1.0f);
            break;

        case AudioManager.AUDIOFOCUS_LOSS:
            // Lost focus for an unbounded amount of time: stop playback and release media player
            if (mMediaPlayer.isPlaying()) mMediaPlayer.stop();
            mMediaPlayer.release();
            mMediaPlayer = null;
            break;

        case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
            // Lost focus for a short time, but we have to stop
            // playback. We don't release the media player because playback
            // is likely to resume
            if (mMediaPlayer.isPlaying()) mMediaPlayer.pause();
            break;

        case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
            // Lost focus for a short time, but it's ok to keep playing
            // at an attenuated level
            if (mMediaPlayer.isPlaying()) mMediaPlayer.setVolume(0.1f, 0.1f);
            break;
    }
}

Keep in mind that the audio focus APIs are available only with API level 8 (Android 2.2)and above, so if you want to support previous versions of Android, you should adopt a backward compatibility strategy that allows you to use this feature if available, and fall back seamlessly if not.

记住音频焦点的api只在api等级8或者更高的等级上可用。所以如果你想要支持前一个版本的Android,你应该采取一个向后兼容的策略,这个策略是如果它可用,就会允许你使用这个功能,如果不可用,无缝回落。

You can achieve backward compatibility either by calling the audio focus methods by reflectionor by implementing all the audio focus features in a separate class (say,AudioFocusHelper). Here is an example of such a class:

你能获得向前兼容,或通过调用音频焦点方法,通过反射,通过在一个单独的实现了所有音频焦点功能的类文件里。这里是这样一个类的例子:

public class AudioFocusHelper implements AudioManager.OnAudioFocusChangeListener {
    AudioManager mAudioManager;

    // other fields here, you'll probably hold a reference to an interface
    // that you can use to communicate the focus changes to your Service

    public AudioFocusHelper(Context ctx, /* other arguments here */) {
        mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
        // ...
    }

    public boolean requestFocus() {
        return AudioManager.AUDIOFOCUS_REQUEST_GRANTED ==
            mAudioManager.requestAudioFocus(mContext, AudioManager.STREAM_MUSIC,
            AudioManager.AUDIOFOCUS_GAIN);
    }

    public boolean abandonFocus() {
        return AudioManager.AUDIOFOCUS_REQUEST_GRANTED ==
            mAudioManager.abandonAudioFocus(this);
    }

    @Override
    public void onAudioFocusChange(int focusChange) {
        // let your service know about the focus change
    }
}

You can create an instance of AudioFocusHelper class only if you detect that the system is running API level 8 or above. For example:

你能创建一个AudioFocusHelper类的实例 , 如果你检测到系统是运行在api等级8 或者更高等级上。例如:

if (android.os.Build.VERSION.SDK_INT >= 8) {
    mAudioFocusHelper = new AudioFocusHelper(getApplicationContext(), this);
} else {
    mAudioFocusHelper = null;
}

你可能感兴趣的:(Android)