一、控制app的音量與播放
使用硬件音量鍵來控制音量
需要在Activity或者Fragment創(chuàng)建的時(shí)候就設(shè)置音量控制,這樣確保不管App是否可見,音頻控制功能都正常工作。
setVolumeControlStream(AudioManager.STREAM_MUSIC);
使用硬件的播放控制按鍵來控制App的音頻播放
無論用戶通過手機(jī)或者線控耳機(jī)等按下哪些控制按鈕,比如播放、暫停,系統(tǒng)都會(huì)廣播一個(gè)帶有ACTION_MEDIA_BUTTON的Intent。
<receiver android:name=".RemoteControlReceiver">
<intent-filter>
<action android:name="android.intent.action.MEDIA_BUTTON" />
</intent-filter>
</receiver>
Receiver需要判斷廣播來自哪個(gè)按鈕
public class RemoteControlReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_MEDIA_BUTTON.equals(intent.getAction())) {
KeyEvent event = (KeyEvent)intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
if (KeyEvent.KEYCODE_MEDIA_PLAY == event.getKeyCode()) {
// Handle key press.
}
}
}
}
如何注冊監(jiān)聽和取消監(jiān)聽
AudioManager am = mContext.getSystemService(Context.AUDIO_SERVICE);
...
// Start listening for button presses
am.registerMediaButtonEventReceiver(RemoteControlReceiver);
...
// Stop listening for button presses
am.unregisterMediaButtonEventReceiver(RemoteControlReceiver);
二、管理音頻焦點(diǎn)
請求獲取音頻焦點(diǎn)
requestAudioFocus() 來獲取到音頻流焦點(diǎn)。
- 短暫的焦點(diǎn)鎖定:當(dāng)期待播放一個(gè)短暫的音頻時(shí)候(比如推送聲音)
- 永久的焦點(diǎn)鎖定:當(dāng)計(jì)劃播放可預(yù)期到的較長的音頻時(shí)候(比如播放音樂)
我們必須在開始播放前請求音頻焦點(diǎn),比如用戶此時(shí)點(diǎn)擊了播放按鈕
AudioManager am = mContext.getSystemService(Context.AUDIO_SERVICE);
...
// Request audio focus for playback
int result = am.requestAudioFocus(afChangeListener,
// Use the music stream.
AudioManager.STREAM_MUSIC,
// Request permanent focus.
AudioManager.AUDIOFOCUS_GAIN);
if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
am.unregisterMediaButtonEventReceiver(RemoteControlReceiver);
// Start playback.
}
一旦結(jié)束播放,需要調(diào)用abandonAudioFocus()方法,通知系統(tǒng)說不再需要獲取焦點(diǎn)并且取消注冊AudioManager.OnAudioFocusChangeListener的焦點(diǎn)。
// Abandon audio focus when playback complete
am.abandonAudioFocus(afChangeListener);
當(dāng)請求短暫音頻焦點(diǎn),我們可以選擇是否開啟"ducking",Ducking機(jī)制可以允許音頻間歇性短暫播放??梢宰屍渌鸄pp繼續(xù)播放,僅在短暫的時(shí)間內(nèi)降低自己的音量。
// Request audio focus for playback
int result = am.requestAudioFocus(afChangeListener,
// Use the music stream.
AudioManager.STREAM_MUSIC,
// Request permanent focus.
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
// Start playback.
}
處理失去音頻焦點(diǎn)
- 失去短暫焦點(diǎn):在這種情況下,暫停當(dāng)前音頻的播放或者降低音量,需要準(zhǔn)備恢復(fù)播放在重新獲取到焦點(diǎn)之后。
- 失去永久焦點(diǎn):假設(shè)另一個(gè)程序開始播放音樂,此時(shí)我們的程序就應(yīng)該徹底結(jié)束。停止播放,放棄自己的音頻焦點(diǎn)。
- Ducking:降低音量,讓其余短暫聲音突出,之后恢復(fù)原音量。
OnAudioFocusChangeListener afChangeListener = new OnAudioFocusChangeListener() {
public void onAudioFocusChange(int focusChange) {
if (focusChange == AUDIOFOCUS_LOSS_TRANSIENT
// Pause playback
//失去短暫焦點(diǎn)
} else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
// Resume playback
//恢復(fù)焦點(diǎn)
} else if (focusChange==AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK
// Lower the volume
} else if (focusChange == AudioManager.AUDIOFOCUS_LOSS) {
am.unregisterMediaButtonEventReceiver(RemoteControlReceiver);
am.abandonAudioFocus(afChangeListener);
// Stop playback
// 失去永久焦點(diǎn)
}
}
};
三、音頻設(shè)備的相關(guān)問題
檢測目前正在使用的硬件設(shè)備
可以使用AudioManager來查詢某個(gè)音頻是否輸出到揚(yáng)聲器,有線耳機(jī)還是藍(lán)牙上。
if (isBluetoothA2dpOn()) {
// Adjust output for Bluetooth.
} else if (isSpeakerphoneOn()) {
// Adjust output for Speakerphone.
} else if (isWiredHeadsetOn()) {
// Adjust output for headsets
} else {
// If audio plays and noone can hear it, is it still playing?
}
處理音頻輸出設(shè)備的改變
當(dāng)耳機(jī)線被拔出,或者藍(lán)牙耳機(jī)連接斷開時(shí),如果在播放音樂/視頻,為了用戶體驗(yàn),避免突如其來的揚(yáng)聲器播放,我們通常做法是暫停此時(shí)正在播放的音樂/視頻。
在這種情況下,系統(tǒng)會(huì)廣播帶有ACTION_AUDIO_BECOMING_NOISY的intent。我們只需要接受這種廣播,對(duì)其進(jìn)行處理即可。
private class NoisyAudioStreamReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (AudioManager.ACTION_AUDIO_BECOMING_NOISY.equals(intent.getAction())) {
// Pause the playback
}
}
}
private IntentFilter intentFilter = new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
private void startPlayback() {
registerReceiver(myNoisyAudioStreamReceiver(), intentFilter);
}
private void stopPlayback() {
unregisterReceiver(myNoisyAudioStreamReceiver);
}