Android 中音频焦点的使用场景及代码示例
文章目录
一、音频焦点简介
在 Android 系统中,音频焦点(Audio Focus)是一种机制,用于管理多个应用程序同时播放音频时的冲突。当一个应用程序请求音频焦点并获得它时,其他应用程序在播放音频时需要做出相应的调整,以避免多个音频同时播放造成混乱。
二、使用场景
(一)音乐播放器
- 当用户正在使用音乐播放器收听歌曲时,若有来电,音乐播放器应暂停播放,以让电话铃声能够清晰地被听到。当电话结束后,音乐播放器可以根据情况恢复播放。
- 若用户在听音乐的过程中打开了另一个音乐类应用,此时正在播放音乐的应用应该暂停或降低音量,以避免两个音乐同时播放。
(二)语音导航应用
- 在用户使用语音导航的同时,如果有音乐播放,导航的语音提示应该能够优先播放,确保用户能够清楚地听到导航指令。
- 当导航语音提示结束后,音乐可以恢复正常播放。
(三)社交类应用的语音消息
- 当用户在收听社交类应用的语音消息时,若有其他音频正在播放,应该暂停或降低其他音频的音量,以便用户能够听清语音消息。
- 语音消息播放完毕后,其他音频可以恢复播放。
三、版本兼容性处理
(一)Android 8.0 及以上版本
在 Android 8.0(API 26)及以上版本中,音频焦点的请求方式发生了变化,推荐使用 AudioFocusRequest 来构建请求:
import android.media.AudioAttributes;
import android.media.AudioFocusRequest;
import android.media.AudioManager;
import android.os.Build;
public class AudioFocusHelper {
private AudioManager audioManager;
private AudioFocusRequest focusRequest;
public AudioFocusHelper(Context context) {
audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
}
public boolean requestAudioFocus() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
AudioAttributes playbackAttributes = new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_MEDIA)
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.build();
focusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
.setAudioAttributes(playbackAttributes)
.setAcceptsDelayedFocusGain(true)
.setOnAudioFocusChangeListener(this::onAudioFocusChange)
.build();
int result = audioManager.requestAudioFocus(focusRequest);
return result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
} else {
int result = audioManager.requestAudioFocus(this::onAudioFocusChange,
AudioManager.STREAM_MUSIC,
AudioManager.AUDIOFOCUS_GAIN);
return result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
}
}
public void abandonAudioFocus() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
if (focusRequest != null) {
audioManager.abandonAudioFocusRequest(focusRequest);
}
} else {
audioManager.abandonAudioFocus(this::onAudioFocusChange);
}
}
}
四、音频焦点与 MediaSession 结合使用
在现代 Android 应用中,推荐将音频焦点与 MediaSession 结合使用,以提供更好的媒体控制体验:
import android.support.v4.media.session.MediaSessionCompat;
import android.support.v4.media.session.PlaybackStateCompat;
public class MediaPlaybackManager {
private MediaSessionCompat mediaSession;
private AudioFocusHelper audioFocusHelper;
private boolean playOnAudioFocus = false;
public MediaPlaybackManager(Context context) {
audioFocusHelper = new AudioFocusHelper(context);
mediaSession = new MediaSessionCompat(context, "MediaPlaybackManager");
setupMediaSession();
}
private void setupMediaSession() {
mediaSession.setCallback(new MediaSessionCompat.Callback() {
@Override
public void onPlay() {
if (audioFocusHelper.requestAudioFocus()) {
mediaSession.setActive(true);
// 开始播放
startPlayback();
} else {
playOnAudioFocus = true;
}
}
@Override
public void onPause() {
// 暂停播放
pausePlayback();
}
@Override
public void onStop() {
audioFocusHelper.abandonAudioFocus();
mediaSession.setActive(false);
// 停止播放
stopPlayback();
}
});
}
private void onAudioFocusChange(int focusChange) {
switch (focusChange) {
case AudioManager.AUDIOFOCUS_GAIN:
if (playOnAudioFocus) {
playOnAudioFocus = false;
startPlayback();
}
// 恢复音量
break;
case AudioManager.AUDIOFOCUS_LOSS:
playOnAudioFocus = false;
stopPlayback();
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
playOnAudioFocus = true;
pausePlayback();
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
// 降低音量
break;
}
}
}
五、音频焦点在前台服务中的使用
对于需要在后台持续播放音频的应用,应该在前台服务中处理音频焦点:
public class AudioPlaybackService extends Service {
private static final int NOTIFICATION_ID = 1;
private AudioFocusHelper audioFocusHelper;
private MediaPlaybackManager mediaPlaybackManager;
@Override
public void onCreate() {
super.onCreate();
audioFocusHelper = new AudioFocusHelper(this);
mediaPlaybackManager = new MediaPlaybackManager(this);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (audioFocusHelper.requestAudioFocus()) {
startForeground(NOTIFICATION_ID, createNotification());
// 开始播放
return START_STICKY;
}
return START_NOT_STICKY;
}
private Notification createNotification() {
// 创建通知
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("正在播放")
.setSmallIcon(R.drawable.ic_music_note)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setOngoing(true);
return builder.build();
}
@Override
public void onDestroy() {
audioFocusHelper.abandonAudioFocus();
stopForeground(true);
super.onDestroy();
}
}
六、音频焦点请求失败的处理策略
在实际应用中,音频焦点请求可能会失败,我们需要有合适的处理策略:
- 立即重试策略:
public boolean requestAudioFocusWithRetry() {
int maxRetries = 3;
int retryCount = 0;
while (retryCount < maxRetries) {
if (requestAudioFocus()) {
return true;
}
retryCount++;
try {
Thread.sleep(100); // 等待100毫秒后重试
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return false;
}
}
return false;
}
- 延迟重试策略:
private void scheduleAudioFocusRetry() {
Handler handler = new Handler(Looper.getMainLooper());
handler.postDelayed(() -> {
if (requestAudioFocus()) {
if (playOnAudioFocus) {
startPlayback();
}
}
}, 1000); // 1秒后重试
}
- 用户触发重试:
public void handleAudioFocusFailure() {
// 显示提示给用户
showPlaybackErrorDialog("无法获取音频焦点,是否重试?",
() -> {
if (requestAudioFocus()) {
startPlayback();
} else {
showToast("获取音频焦点失败");
}
});
}
通过以上示例,我们可以看到音频焦点在 Android 应用中的重要性及其实现方式。合理使用音频焦点可以让应用提供更好的用户体验,与其他应用和谐共存。在实际开发中,建议根据具体场景选择合适的实现方式,并注意版本兼容性处理。