一、控制app的音量与播放
使用硬件音量键来控制音量
需要在Activity或者Fragment创建的时候就设置音量控制,这样确保不管App是否可见,音频控制功能都正常工作。
setVolumeControlStream(AudioManager.STREAM_MUSIC);
使用硬件的播放控制按键来控制App的音频播放
无论用户通过手机或者线控耳机等按下哪些控制按钮,比如播放、暂停,系统都会广播一个带有ACTION_MEDIA_BUTTON的Intent。
<receiver android:name=".RemoteControlReceiver">
<intent-filter>
<action android:name="android.intent.action.MEDIA_BUTTON" />
</intent-filter>
</receiver>
Receiver需要判断广播来自哪个按钮
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.
}
}
}
}
如何注册监听和取消监听
AudioManager am = mContext.getSystemService(Context.AUDIO_SERVICE);
...
// Start listening for button presses
am.registerMediaButtonEventReceiver(RemoteControlReceiver);
...
// Stop listening for button presses
am.unregisterMediaButtonEventReceiver(RemoteControlReceiver);
二、管理音频焦点
请求获取音频焦点
requestAudioFocus() 来获取到音频流焦点。
- 短暂的焦点锁定:当期待播放一个短暂的音频时候(比如推送声音)
- 永久的焦点锁定:当计划播放可预期到的较长的音频时候(比如播放音乐)
我们必须在开始播放前请求音频焦点,比如用户此时点击了播放按钮
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.
}
一旦结束播放,需要调用abandonAudioFocus()方法,通知系统说不再需要获取焦点并且取消注册AudioManager.OnAudioFocusChangeListener的焦点。
// Abandon audio focus when playback complete
am.abandonAudioFocus(afChangeListener);
当请求短暂音频焦点,我们可以选择是否开启"ducking",Ducking机制可以允许音频间歇性短暂播放。可以让其他App继续播放,仅在短暂的时间内降低自己的音量。
// 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.
}
处理失去音频焦点
- 失去短暂焦点:在这种情况下,暂停当前音频的播放或者降低音量,需要准备恢复播放在重新获取到焦点之后。
- 失去永久焦点:假设另一个程序开始播放音乐,此时我们的程序就应该彻底结束。停止播放,放弃自己的音频焦点。
- Ducking:降低音量,让其余短暂声音突出,之后恢复原音量。
OnAudioFocusChangeListener afChangeListener = new OnAudioFocusChangeListener() {
public void onAudioFocusChange(int focusChange) {
if (focusChange == AUDIOFOCUS_LOSS_TRANSIENT
// Pause playback
//失去短暂焦点
} else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
// Resume playback
//恢复焦点
} 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
// 失去永久焦点
}
}
};
三、音频设备的相关问题
检测目前正在使用的硬件设备
可以使用AudioManager来查询某个音频是否输出到扬声器,有线耳机还是蓝牙上。
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?
}
处理音频输出设备的改变
当耳机线被拔出,或者蓝牙耳机连接断开时,如果在播放音乐/视频,为了用户体验,避免突如其来的扬声器播放,我们通常做法是暂停此时正在播放的音乐/视频。
在这种情况下,系统会广播带有ACTION_AUDIO_BECOMING_NOISY的intent。我们只需要接受这种广播,对其进行处理即可。
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);
}