在给定的时间尽管只有一个活动可以运行,但Android是一个多任务环境。这对应用程序使用音频造成了一个特别大的难度,由于只有一个音频输出,可能会有好几个媒体服务争夺使用它。

Android2.2之前,没有内置机制来解决这个问题,这可能在某些情况下导致糟糕的用户体验。例如,一个用户正在听音乐,同时另一个应用程序有很重要的事需要通知用户,由于吵闹的音乐用户可能不会听到提示音。从Android2.2开始,Android平台为应用程序提供了一个方式来协商设备的音频输出,这个机制被称为音频焦点

当您的应用程序需要输出音频,如音乐或一个通知,这时你就必须请求音频焦点。一旦得到焦点,它就可以自由的使用声音输出设备,同时它会不断监听焦点的更改。如果它被通知已经失去了音频焦点,它会要么立即杀死音频或立即降低到一个安静的水平(被称为“ducking”——有一个标记,指示哪一个是适当的)当它再次接收焦点时,继续不断播放。 

音频焦点是自然的合作,应用程序都期望(强烈鼓励)遵守音频焦点指南,但规则并不是系统强制执行的。如果应用程序失去音频焦点后想要播放嘈杂的音乐,在系统中没有什么会阻止他。然而,这样可能会让用户有更糟糕的体验,并可能卸载这运行不当的应用程序。

请求音频焦点,您必须从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.}

requestAudioFocus()的第一个参数是AudioManager.OnAudioFocusChangeListener,每当音频焦点有变动的时候其onAudioFocusChange()方法被调用。

您还应该在你的服务和活动上实现这个接口。 代码如下:

class MyService extends Service

implements AudioManager.OnAudioFocusChangeListener {

// ....

public void onAudioFocusChange(int focusChange) {

// Do something based on focus change...

}

}

focusChange参数告诉你音频焦点是如何改变的,并且可以使用以下的值之一(他们都是在AudioManager中定义常量的): 

AUDIOFOCUS_GAIN: 你已经得到了音频焦点。

AUDIOFOCUS_LOSS:你已经失去了音频焦点很长时间了。你必须停止所有的音频播放。因为你应该不希望长时间等待焦点返回,这将是你尽可能清除你的资源的一个好地方。例如,你应该释放MediaPlayer。

AUDIOFOCUS_LOSS_TRANSIENT:你暂时失去了音频焦点,但很快会重新得到焦点。你必须停止所有的音频播放,但是你可以保持你的资源,因为你可能很快会重新获得焦点。

AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:你暂时失去了音频焦点,但你可以小声地继续播放音频(低音量)而不是完全扼杀音频。

下面是一个示例实现:

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 andrelease 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 becauseplayback

// 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;

}

}

记住,音频焦点APIs在API级别8(Android2.2)及以上才有效,所以如果你想要支持的以前版本的Android,(如果有的话)你应该采取一种向后兼容性策略,允许您使用该特性,(如果没有的话),只能选择8以后的版本。 

通过反射调用音频焦点方法或通过在一个单独类中实现所有的音频焦点特性,您可以实现向后兼容性(AudioFocusHelper中阐明)。下面是这样一个类的示例:

public class AudioFocusHelper implementsAudioManager.OnAudioFocusChangeListener {

AudioManager mAudioManager;

// other fields here, you'll probably hold a reference to aninterface

// that you can use to communicate the focus changes to yourService


public AudioFocusHelper(Context ctx, ) {

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

}

}

当你发现系统运行时API级别在8级或以上时,您可以创建AudioFocusHelper 类的一个实例,例如:

Java代码

if (android.os.Build.VERSION.SDK_INT >= 8) {

mAudioFocusHelper = new AudioFocusHelper(getApplicationContext(),this);

} else {

mAudioFocusHelper = null;

}