理解音频焦点

 

Android手机支持多个应用同时播放音频。操作系统会把多个音频流混合在一起播放,但是多个应用同时播放音频,给用户带来的体验往往不佳。为了提供更友好的用户体验,Android提供了一个API,让应用程序可以共享音频焦点,旨在保证同一时段内只有一个应用可以维持音频聚焦。

本系列文章旨在让您深入理解音频焦点的含义,使用方法和其对用户体验的重要性。本篇文章是该系列的第一部分,该系列三篇文章包含了:

  1. 最常见的音频焦点用例和成为一个优秀的媒体事业人员的重要性(此篇文章
  2. 其它一些能体现音频焦点对应用体验的重要性的用例
  3. 在您的应用中实现音频焦点的三个步骤

音频焦点的良好协作性,主要依赖于应用程序是否遵循音频焦点指南,操作系统没有强制执行音频焦点的规范来约束应用程序,如果应用选择在失去音频焦点后继续大声播放音频,会带来不良的用户体验,可能直接导致应户卸载应用,但这是无法阻止的行为,只能靠开发者自我约束。

下面是一些音频焦点使用场景(假设用户正在使用您的应用播放音频)。

当您的应用需要播放声音的时候,应该先请求音频聚焦,在获得音频焦点后再播放声音。

用例一 : 用户在使用您的应用播放音频1时,打开另一个应用并尝试播放该应用相关的音频2

您的应用不处理音频焦点的情况下:

您的音频1和另一个应用的音频2会重叠播放,用户无法正常听到来自任何应用的音频,这样的用户体验很不友好。

 

您的应用处理了音频焦点的情况下:

在另一个应用需要播放音频时,它会请求音频焦点常驻,即音频永久聚焦。一旦系统授权,它便会开始播放音频,这时候您的应用需要响应音频焦点的丢失通知,停止播放。这样用户就只会听到另一个应用的音频。

 

同样的道理,假如过了五分钟,您的应用需要播放音频,您同样需要申请音频焦点,一旦获得系统授权,我们就可以开始播放音频,其它应用响应音频焦点丢失通知,停止播放。

用例二 : 当您播放音频时候,正好手机来电,需要播放响铃。

您的应用不处理音频焦点的情况下:

手机响铃后,用户会听到铃声和您的手机音频叠加在一起播放。如果用户选择直接挂断电话,您的音频会保持播放。如果用户选择接通电话,他会听到通话声音和您的应用音频叠加在一起播放,挂断通话后您的应用音频会保持播放。无论如何,您的应用音频将全程保持播放状态。这带来的通话体验极差。

 

您的应用处理了音频焦点的情况下:

当手机响铃(您还未接通电话), 您的应用应该选择相应的回避(这是系统应用的要求)措施来响应短暂的音频焦点丢失。回避的措施可以是把应用的音量降低到百分之二十,也可以是直接暂停播放(如果您的应用是播客类,语音类应用)。

  • 如果用户拒绝接听电话,您的应用可以马上采取响应音频焦点的获取,然后做出提高音量或恢复播放的相关操作。
  • 如果用户接听了电话,操作系统会发出音频焦点丢失的通知。您的应用应该选择暂停播放,然后在通话结束后恢复播放。

 

代码实例

public class MainActivity extends Activity {

    private AudioManager mAudioManager;
    private AudioFocusRequest mFocusRequest;
    private AudioManager.OnAudioFocusChangeListener mListener;
    private AudioAttributes mAttribute;
    @SuppressLint("HandlerLeak")
    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
        mListener = new AudioManager.OnAudioFocusChangeListener() {
            @Override
            public void onAudioFocusChange(int focusChange) {
                switch (focusChange) {
                    case AudioManager.AUDIOFOCUS_GAIN:
                       // TBD 继续播放
                        break;
                    case AudioManager.AUDIOFOCUS_LOSS:
                       // TBD 停止播放
                        break;
                    case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
                        // TBD 暂停播放
                        break;
                    case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
                        // TBD 混音播放 
                        break;
                    default:
                        break;
                }

            }
        };
        //android 版本 5.0
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            mAttribute = new AudioAttributes.Builder()
                    .setUsage(AudioAttributes.USAGE_MEDIA)
                    .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
                    .build();
        }
        //android 版本 8.0
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
            mFocusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
                    .setWillPauseWhenDucked(true)
                    .setAcceptsDelayedFocusGain(true)
                    .setOnAudioFocusChangeListener(mListener, mHandler)
                    .setAudioAttributes(mAttribute)
                    .build();
        }
    }

    private void requestAudioFocus() {

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            int ret = mAudioManager.requestAudioFocus(mFocusRequest);       
            if (ret == AudioManager.AUDIOFOCUS_REQUEST_FAILED) {
                // TBD 焦点申请失败 不执行播放
            } else if (ret == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
                // TBD 焦点申请成功 执行播放
            } else if (ret == AudioManager.AUDIOFOCUS_REQUEST_DELAYED) {
                // TBD 焦点申请delay 不执行播放(这种时候一般电话中,我们播放音乐会有这种状态,如果ret是delay,那么如果可以播放的时候会收到对应AUDIOFOCUS_GAIN的callback)
            }
        }
    }

    private void abandonAudioFocus() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            mAudioManager.abandonAudioFocusRequest(mFocusRequest);
        }

    }
}

以上就是App使用AudioFocus的一个简单demo,那么我们在使用的时候申请一个什么类型的焦点呢?有几个值的含义是一定要明确的:
AUDIOFOCUS_GAIN:长时间获取焦点,一般用于音视频。
AUDIOFOCUS_GAIN_TRANSIENT:短暂获得,一般用于电话,语音助理等
AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK:混音,一般用于导航
AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE:(android后加的)与AUDIOFOCUS_GAIN_TRANSIENT类似,表示一个短暂的获取焦点,一般用于语音识别什么的,很少用。
既然提到了电话这里先吐槽下谷歌做的音频焦点,那是真的烂,因此才会有那么多的厂商来定制音频焦点这块,本来一个requestAudioFocus就可以了,但发现电话时好像不应该被其他应用抢去焦点,那可咋整,哦,加个接口吧,于是乎 public void requestAudioFocusForCall(int streamType, int durationHint) 来了,requestAudioFocus(AudioFocusRequest requset)被更新来更新去, requestAudioFocusForCall(int streamType, int durationHint) 貌似出了之后发现不是很好在就一直没有更新过,requestAudioFocusForCall优先级最高,也不需要返回值,方法执行成功与否也不知道,对应abandonAudioFocusForCall()也是简单粗暴。其实个人觉得requestAudioFocusForCall(int streamType, int durationHint)这个durationHint基本可以写死AUDIOFOCUS_GAIN_TRANSIENT,这样岂不更简单粗暴。
说的有点跑题,综上,根据这些值含义,我们基本知道了我们要申请什么样的音频焦点。

总结

当您的应用需要输出音频时,应该请求音频焦点。只有在获得音频焦点后,才能开始播放。但是,在播放过程中可能无法把音频焦点一直据为己有,因为其它应用程序可以发出音频焦点的请求来抢占音频焦点,这种情况下,您的应用可以选择暂停播放或者降低音量,这样用户才能更清晰地听到其它应用程序的音频。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值