1. 音频焦点请求与释放
任务说明
实现一个最小可运行的音频播放器,在开始播放前通过 AudioManager 请求音频焦点,在暂停或停止时释放焦点,并处理焦点丢失(AUDIOFOCUS_LOSS)事件——当其他应用抢走焦点时自动暂停播放。本案例聚焦于焦点管理逻辑本身,不涉及音频解码与渲染细节,使用 ToneGenerator 产生提示音作为可感知的播放状态。
核心实现
// AudioFocusPlayer.java
import android.media.AudioManager;
import android.media.ToneGenerator;
import android.content.Context;
public class AudioFocusPlayer {
private final AudioManager audioManager;
private final ToneGenerator toneGenerator;
private boolean isPlaying = false;
// 焦点监听器:处理焦点丢失事件
private final AudioManager.OnAudioFocusChangeListener focusChangeListener =
new AudioManager.OnAudioFocusChangeListener() {
@Override
public void onAudioFocusChange(int focusChange) {
switch (focusChange) {
case AudioManager.AUDIOFOCUS_LOSS:
// 长时间失去焦点:暂停并释放焦点
pausePlayback();
abandonFocus();
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
// 短暂失去焦点(如来电):暂停但不释放
pausePlayback();
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
// 可闪避:降低音量(ToneGenerator 不支持闪避,直接忽略)
break;
}
}
};
public AudioFocusPlayer(Context context) {
audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
toneGenerator = new ToneGenerator(AudioManager.STREAM_MUSIC, 80);
}
// 请求焦点并开始播放
public boolean startPlayback() {
if (isPlaying) return true;
int result = audioManager.requestAudioFocus(focusChangeListener,
AudioManager.STREAM_MUSIC,
AudioManager.AUDIOFOCUS_GAIN);
if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
isPlaying = true;
// 持续播放 1kHz 提示音,模拟播放状态
toneGenerator.startTone(ToneGenerator.TONE_CDMA_PIP, 1500);
return true;
}
return false; // 焦点请求被拒绝,不播放
}
// 暂停播放(保留焦点)
public void pausePlayback() {
if (isPlaying) {
toneGenerator.stopTone();
isPlaying = false;
}
}
// 停止播放并释放焦点
public void stopPlayback() {
pausePlayback();
abandonFocus();
}
// 释放音频焦点
private void abandonFocus() {
audioManager.abandonAudioFocus(focusChangeListener);
}
// 资源释放
public void release() {
stopPlayback();
toneGenerator.release();
}
}
调用示例
// 在 Activity 中使用
AudioFocusPlayer player = new AudioFocusPlayer(this);
// 播放:请求焦点,成功后才开始
boolean granted = player.startPlayback();
if (!granted) {
// 焦点被拒,提示用户当前无法播放
}
// 用户点击暂停
player.pausePlayback();
// 用户点击停止
player.stopPlayback();
// Activity 销毁时释放资源
@Override
protected void onDestroy() {
super.onDestroy();
player.release();
}
焦点状态与事件流
验证方法
判定标准:在真机或模拟器上安装运行,依次执行以下操作并观察行为——
- 正常播放:调用
startPlayback()后能听到提示音,说明焦点请求成功且播放已开始; - 焦点被抢占:播放期间打开系统音乐应用并播放歌曲,本应用提示音应立即停止(
AUDIOFOCUS_LOSS触发pausePlayback()); - 焦点释放:调用
stopPlayback()后,其他应用可正常获得音频焦点(不被本应用阻塞); - 暂停不释放:调用
pausePlayback()后提示音停止,但焦点仍被持有,此时其他应用请求焦点会触发AUDIOFOCUS_LOSS回调。
通过 adb shell dumpsys audio 可查看当前焦点持有者,验证释放逻辑是否正确。