Android 音頻焦點(diǎn)(Audio Focus)
引子
說(shuō) Audio Focus 前先說(shuō)個(gè)很簡(jiǎn)單需求:來(lái)電時(shí)暫停正在播放的音樂(lè)双泪,電話結(jié)束時(shí)恢復(fù)播放争便。
音頻焦點(diǎn)
問(wèn)題的解決方法就是:請(qǐng)求系統(tǒng)的音頻焦點(diǎn)(Request the Audio Focus)。
官方文檔指出Android 在處理音頻播放是分了多個(gè)“音頻流”的稽犁,如音樂(lè)流、音效流、電話聲音流等蛙酪,使控制音量時(shí)可以互不干涉。多數(shù)情況下我們播放音樂(lè)都是使用 STREAM_MUSIC 音頻流翘盖。
另外桂塞,系統(tǒng)中可能會(huì)有多個(gè)應(yīng)用程序會(huì)播放音頻,所以需要考慮他們之間該如何協(xié)調(diào)馍驯,為了避免同時(shí)播放音樂(lè)阁危,Android 系統(tǒng)使用音頻焦點(diǎn)來(lái)進(jìn)行統(tǒng)一管理,即只有獲得了音頻焦點(diǎn)的應(yīng)用程序才可以播放音樂(lè)汰瘫。
那么狂打,播放音頻應(yīng)該這樣來(lái)做:
獲取音頻焦點(diǎn) requestAudioFocus
獲取成功后,開(kāi)始播放音頻
處理音頻焦點(diǎn)的丟失和“DUCK”
播放完畢后取消焦點(diǎn)
如此便可以完美的解決引子里的需求混弥。
一個(gè)簡(jiǎn)單的示例
MusicService.java
public class MusicService extends Service {
? ? private AudioManager mAm;
? ? private boolean isPlaymusic;
? ? private String url;
? ? private MediaPlayer mediaPlayer;
? ? @Override
? ? public void onCreate() {
? ? ? ? super.onCreate();
? ? ? ? mAm = (AudioManager) getSystemService(AUDIO_SERVICE);
? ? }
? ? @Override
? ? public void onStart(Intent intent, int startId) {
? ? ? ? if (intent != null) {
? ? ? ? ? ? Bundle bundle = intent.getExtras();
? ? ? ? ? ? if (bundle != null) {
? ? ? ? ? ? ? ? isPlaymusic = bundle.getBoolean("isPlay", true);
? ? ? ? ? ? ? ? url = bundle.getString("url");
? ? ? ? ? ? ? ? if (isPlaymusic)
? ? ? ? ? ? ? ? ? ? play();
? ? ? ? ? ? ? ? else
? ? ? ? ? ? ? ? ? ? stop();
? ? ? ? ? ? }
? ? ? ? }
? ? }
? ? OnAudioFocusChangeListener afChangeListener = new OnAudioFocusChangeListener() {
? ? public void onAudioFocusChange(int focusChange) {
? ? if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT) {
? ? // Pause playback
? ? pause();
} else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
// Resume playback
resume();
} else if (focusChange == AudioManager.AUDIOFOCUS_LOSS) {
// mAm.unregisterMediaButtonEventReceiver(RemoteControlReceiver);
mAm.abandonAudioFocus(afChangeListener);
// Stop playback
stop();
}
}
};
private boolean requestFocus() {
// Request audio focus for playback
int result = mAm.requestAudioFocus(afChangeListener,
// Use the music stream.
AudioManager.STREAM_MUSIC,
// Request permanent focus.
AudioManager.AUDIOFOCUS_GAIN);
return result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
}
private void resume() {
if (mediaPlayer != null) {
mediaPlayer.start();
}
}
private void pause() {
if (mediaPlayer != null && mediaPlayer.isPlaying()) {
mediaPlayer.pause();
}
}
OnCompletionListener completionListener = new OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer player) {
if(!player.isLooping()){
mAm.abandonAudioFocus(afChangeListener);
}
}
};
private void play() {
if (requestFocus()) {
if (mediaPlayer == null) {
try {
mediaPlayer = new MediaPlayer();
mediaPlayer.setDataSource(url);
mediaPlayer.prepare();
mediaPlayer.setOnCompletionListener(completionListener);
} catch (IOException e) {
e.printStackTrace();
}
}
if (!mediaPlayer.isPlaying()) {
mediaPlayer.start();
}
}
}
@Override
public void onDestroy() {
super.onDestroy();
if (mediaPlayer != null)
mediaPlayer.release();
}
private void stop() {
if (mediaPlayer != null) {
mediaPlayer.stop();
}
}
@Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
return null;
}
}
經(jīng)模擬器測(cè)試趴乡,當(dāng)來(lái)電時(shí)音頻焦點(diǎn)會(huì)給到鈴聲流,并打出日志:
I/AudioService(1235):? AudioFocus? requestAudioFocus() from AudioFocus_For_Phone_Ring_And_Calls
此時(shí)MusicService中的afChangeListener會(huì)得到AUDIOFOCUS_LOSS_TRANSIENT,于是會(huì)暫停播放音頻晾捏。
當(dāng)通話結(jié)束或者掛掉電話官辽,afChangeListener會(huì)得到AUDIOFOCUS_GAIN,于是恢復(fù)播放音頻粟瞬。
注意:
播放完畢一定要禁止掉請(qǐng)求的音頻焦點(diǎn)abandonAudioFocus(afChangeListener)同仆,否則,如果播放完畢后的某個(gè)時(shí)段剛好有個(gè)通話結(jié)束裙品,并且此時(shí)沒(méi)有其他的應(yīng)用占用了焦點(diǎn)俗批,系統(tǒng)會(huì)重新通知服務(wù)里的afChangeListener,導(dǎo)致音頻再次的播放市怎。
如果丟失的短暫音頻焦點(diǎn)允許DUCK狀態(tài)AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK岁忘,在這種情況下,應(yīng)用程序降低音量繼續(xù)播放区匠,不需要暫停干像。再次獲取后,恢復(fù)原來(lái)的音量驰弄。