Android 中音频焦点的使用场景及示例

Android 中音频焦点的使用场景及代码示例

本文首发地址 https://editor.csdn.net/md/?articleId=142345787

一、音频焦点简介

在 Android 系统中,音频焦点(Audio Focus)是一种机制,用于管理多个应用程序同时播放音频时的冲突。当一个应用程序请求音频焦点并获得它时,其他应用程序在播放音频时需要做出相应的调整,以避免多个音频同时播放造成混乱。

二、使用场景

(一)音乐播放器

  1. 当用户正在使用音乐播放器收听歌曲时,若有来电,音乐播放器应暂停播放,以让电话铃声能够清晰地被听到。当电话结束后,音乐播放器可以根据情况恢复播放。
  2. 若用户在听音乐的过程中打开了另一个音乐类应用,此时正在播放音乐的应用应该暂停或降低音量,以避免两个音乐同时播放。

(二)语音导航应用

  1. 在用户使用语音导航的同时,如果有音乐播放,导航的语音提示应该能够优先播放,确保用户能够清楚地听到导航指令。
  2. 当导航语音提示结束后,音乐可以恢复正常播放。

(三)社交类应用的语音消息

  1. 当用户在收听社交类应用的语音消息时,若有其他音频正在播放,应该暂停或降低其他音频的音量,以便用户能够听清语音消息。
  2. 语音消息播放完毕后,其他音频可以恢复播放。

三、版本兼容性处理

(一)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();
    }
}

六、音频焦点请求失败的处理策略

在实际应用中,音频焦点请求可能会失败,我们需要有合适的处理策略:

  1. 立即重试策略:
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;
}
  1. 延迟重试策略:
private void scheduleAudioFocusRetry() {
    Handler handler = new Handler(Looper.getMainLooper());
    handler.postDelayed(() -> {
        if (requestAudioFocus()) {
            if (playOnAudioFocus) {
                startPlayback();
            }
        }
    }, 1000); // 1秒后重试
}
  1. 用户触发重试:
public void handleAudioFocusFailure() {
    // 显示提示给用户
    showPlaybackErrorDialog("无法获取音频焦点,是否重试?",
        () -> {
            if (requestAudioFocus()) {
                startPlayback();
            } else {
                showToast("获取音频焦点失败");
            }
        });
}

通过以上示例,我们可以看到音频焦点在 Android 应用中的重要性及其实现方式。合理使用音频焦点可以让应用提供更好的用户体验,与其他应用和谐共存。在实际开发中,建议根据具体场景选择合适的实现方式,并注意版本兼容性处理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

清霜辰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值