1. 构建音频焦点请求与释放框架
任务说明
在Android音视频应用中,多个播放器可能同时运行(如音乐播放器与导航语音),系统通过音频焦点(Audio Focus)机制协调音频流的使用权。本案例实现一个可复用的音频焦点管理框架,支持请求、释放、监听焦点变化并自动处理短暂丢失与永久丢失两种典型场景。
核心实现
// AudioFocusManager.java
package com.example.audiofocus;
import android.content.Context;
import android.media.AudioAttributes;
import android.media.AudioFocusRequest;
import android.media.AudioManager;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
public class AudioFocusManager {
private static final String TAG = "AudioFocusManager";
private final AudioManager audioManager;
private final FocusListener focusListener;
private AudioFocusRequest focusRequest;
private boolean hasFocus = false;
private final Handler mainHandler = new Handler(Looper.getMainLooper());
// 焦点变化回调接口
public interface FocusListener {
void onFocusGained(); // 获得焦点(可开始播放)
void onFocusLostTransient(); // 短暂丢失(如来电,可暂停)
void onFocusLostPermanent(); // 永久丢失(如其他应用抢占,应停止)
void onFocusDucked(); // 闪避(降低音量而非暂停)
}
public AudioFocusManager(Context context, FocusListener listener) {
this.audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
this.focusListener = listener;
buildFocusRequest();
}
private void buildFocusRequest() {
AudioAttributes attrs = new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_MEDIA)
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.build();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
focusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
.setAudioAttributes(attrs)
.setAcceptsDelayedFocusGain(true)
.setWillPauseWhenDucked(false)
.setOnAudioFocusChangeListener(changeListener, mainHandler)
.build();
}
}
private final AudioManager.OnAudioFocusChangeListener changeListener =
new AudioManager.OnAudioFocusChangeListener() {
@Override
public void onAudioFocusChange(int focusChange) {
switch (focusChange) {
case AudioManager.AUDIOFOCUS_GAIN:
hasFocus = true;
focusListener.onFocusGained();
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
hasFocus = false;
focusListener.onFocusLostTransient();
break;
case AudioManager.AUDIOFOCUS_LOSS:
hasFocus = false;
focusListener.onFocusLostPermanent();
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
focusListener.onFocusDucked();
break;
}
}
};
/** 请求音频焦点 */
public boolean requestFocus() {
if (hasFocus) return true;
int result;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
result = audioManager.requestAudioFocus(focusRequest);
} else {
// 低版本兼容(API 26以下)
result = audioManager.requestAudioFocus(changeListener,
AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
}
hasFocus = (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
Log.d(TAG, "requestFocus result: " + result);
return hasFocus;
}
/** 释放音频焦点 */
public void abandonFocus() {
if (!hasFocus) return;
int result;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
result = audioManager.abandonAudioFocusRequest(focusRequest);
} else {
result = audioManager.abandonAudioFocus(changeListener);
}
hasFocus = false;
Log.d(TAG, "abandonFocus result: " + result);
}
public boolean isHasFocus() {
return hasFocus;
}
}
使用示例
// MainActivity.java 关键片段
AudioFocusManager focusManager = new AudioFocusManager(this, new AudioFocusManager.FocusListener() {
@Override
public void onFocusGained() {
// 开始或恢复播放
mediaPlayer.start();
}
@Override
public void onFocusLostTransient() {
// 暂停播放,等待重新获得焦点
mediaPlayer.pause();
}
@Override
public void onFocusLostPermanent() {
// 停止播放并释放资源
mediaPlayer.stop();
mediaPlayer.release();
}
@Override
public void onFocusDucked() {
// 降低音量至20%
mediaPlayer.setVolume(0.2f, 0.2f);
}
});
// 用户点击播放按钮时
focusManager.requestFocus();
// 用户点击停止或退出时
focusManager.abandonFocus();
验证方式
在真机上安装应用,准备两个音频播放源(如系统音乐播放器与本应用)。操作步骤:
- 启动本应用并点击播放,确认日志输出
requestFocus result: 1(1表示AUDIOFOCUS_REQUEST_GRANTED),声音正常播放; - 启动系统音乐播放器并播放,本应用应收到
onFocusLostPermanent回调,播放停止; - 关闭系统音乐播放器,再次点击本应用播放,应重新获得焦点并恢复播放;
- 在应用播放期间来电(或使用adb模拟短暂焦点丢失),应收到
onFocusLostTransient回调并暂停,挂断后自动恢复。
判定标准:以上4个场景中回调触发顺序与日志输出符合预期,且无崩溃或ANR,即验证通过。