短焦点释放android_android获取焦点释放焦点 - CSDN
  • 求教,关于音频处理问题,我的背景... 的指点,说要在音效播放完后再让背景音乐重新获取焦点(应该是再requestAudioFocus一次?).但是现在这个我想问一下,我音效跟背景音乐都不是放在同一个java文件里面的,那我要怎么去让背

    求教,关于音频处理问题,我的背景音乐播放的时候,使用的AudioFocus的方法来处理,当我有其他音效要播放的时候,我就把背景音乐改小,但是这就出来了问题,改小了就不会变回原本的音量了,经过@四方城 的指点,说要在音效播放完后再让背景音乐重新获取焦点(应该是再requestAudioFocus一次?).但是现在这个我想问一下,我音效跟背景音乐都不是放在同一个java文件里面的,那我要怎么去让背景重新获取焦点?把背景音乐的mediaplayer对象设成公开的来修改?还是设置成静态来修改?还是有其他什么方法吗?

    好吧,还是我来抽时间给你回答下,先给你贴一段Sample Code吧,你把下面的代码跑起来之后,打电话进来试试看,然后不接,把电话挂了。这是为了模拟AudioFocus的获取,丢失与重新获取。

    【1】Sample Code

    public class MainActivity extends Activity{
        
        public AudioManager mAudioManager;
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            
            mAudioManager = (AudioManager) this.getSystemService(Context.AUDIO_SERVICE);
        }
    
        @Override
        protected void onResume() {
            // TODO Auto-generated method stub
            super.onResume();
            
            Button button1 = (Button)this.findViewById(R.id.button1);
            button1.setOnClickListener(new View.OnClickListener() {
                
                public void onClick(View v) {
                    // TODO Auto-generated method stub
                    mAudioManager.requestAudioFocus(mAudioFocusListener,
                            AudioManager.STREAM_MUSIC,AudioManager.AUDIOFOCUS_GAIN);
                    Log.d("Test", "requestAudioFocus: onClick");
                }
            });
            
            Button button2 = (Button)this.findViewById(R.id.button2);
            button2.setOnClickListener(new View.OnClickListener() {
                
                public void onClick(View v) {
                    // TODO Auto-generated method stub
                    mAudioManager.abandonAudioFocus(mAudioFocusListener);
                    Log.d("Test", "abandonAudioFocus: onClick");
                }
            });
            
            
        }
        
        public AudioManager.OnAudioFocusChangeListener mAudioFocusListener = new AudioManager.OnAudioFocusChangeListener() {
            public void onAudioFocusChange(int focusChange) {
                // AudioFocus is a new feature: focus updates are made verbose on purpose
                switch (focusChange) {
                    case AudioManager.AUDIOFOCUS_LOSS:
                        Log.d("Test", "AudioFocus: received AUDIOFOCUS_LOSS");
                        break;
                    case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
                        Log.d("Test", "AudioFocus: received AUDIOFOCUS_LOSS_TRANSIENT");
                        break;
                    case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
                        Log.d("Test", "AudioFocus: received AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK");
                        break;
                    case AudioManager.AUDIOFOCUS_GAIN:
                        Log.d("Test", "AudioFocus: received AUDIOFOCUS_GAIN");
                        break;
                    default:
                        Log.d("Test", "Unknown audio focus change code");
                }
            }
        };
    }

    【2】那么你看的Log应该是:
    电话打进来的时候出现: AudioFocus: received AUDIOFOCUS_LOSS_TRANSIENT
    电话挂掉的时候出现:AudioFocus: received AUDIOFOCUS_GAIN

    【3】所以,你现在应该理解了。至于具体它是如何在另外一个App释放的时候,自己重新获取的,需要去看AudioManger的原代码。在你不了解它为何会这样的时候,写个小的程序验证一下,就知道了。

    【4】Addition : 其实Android没有强制每个App必须去遵守AudioFocus的使用规范,可能会出现有几个App同时发出声音的情况。但是大多数App都需要自动去那样做,需要的时候请求,用完的时候释放,这样才能保证有良好的使用体验。

    展开全文
  • Android处理音频焦点变化事件一、目标二、实现过程1. 请求音频焦点2. 释放音频焦点3. 处理音频焦点变化三、开发过程回顾四、接下来五、Finally 在《Android处理音频焦点AudioFocus》一文中,介绍了如何请求和释放...

    在《Android处理音频焦点AudioFocus》一文中,介绍了如何请求和释放音频焦点。

    接下来是在神马笔记中的具体应用。

    一、目标

    处理音频焦点变化事件,防止多个音源同时播放。

    二、实现过程

    1. 请求音频焦点

    在开始播放声音之前,需要考虑请求哪一种形式的音频焦点。

    如果只是播放一段非常短的声音,比如酷狗音乐的启动音"Hello, kugo!",可以不用请求音频焦点。

    因为时间很短并且不是特别重要的提示,因此可以不用请求音频焦点。

    神马笔记中播放声音的几个位置。

    位置 生命周期 是否循环 音频焦点
    音乐笔记 Activity,Activity销毁时自动停止。 循环 AUDIOFOCUS_GAIN
    语音笔记 可见时,界面不可见时自动停止。 单次 AUDIOFOCUS_GAIN_TRANSIENT
    视频笔记 可见时,界面不可见时自动停止。 单次 AUDIOFOCUS_GAIN
    录音 可见时,界面不可见时自动停止。 单次 AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE
    录音回放 可见时,界面不可见时自动停止。 单次 AUDIOFOCUS_GAIN_TRANSIENT

    录音时不允许有其他声音,因此使用AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE

    2. 释放音频焦点

    暂停及停止播放声音时,需要及时释放音频焦点。

    3. 处理音频焦点变化

    位置 GAIN LOSS LOSS_TRANSIENT TRANSIENT_CAN_DUCK
    音乐笔记 继续 暂停 暂停 暂停
    语音笔记 / 暂停 暂停 暂停
    视频笔记
    录音 / 停止 停止 停止
    录音回放 / 暂停 暂停 暂停

    TRANSIENT_CAN_DUCK虽然可以调低音量保持播放状态。

    当前简化处理,与LOSS的处理方式保持一致,停止或者暂停。

    三、开发过程回顾

    正确地处理音频焦点变化事件可以很好的提供用户的听歌体验。

    测试过程中发现酷我音乐酷狗音乐并未合适的处理音频焦点事件,体验确实糟糕。

    从选择音乐文件添加到笔记中并实现音乐笔记的界面,

    接着实现了调用第三方应用播放音乐,

    然后是笔记内播放音乐,笔记内播放音乐时会停止视频播放和其他音乐笔记及暂停语音笔记。

    最后处理了音频焦点事件,提升了用户听音乐的体验。

    中间还有一个小插曲,转换了网易云音乐的NCM格式文件。

    四、接下来

    发布神马笔记 版本2.9.0——音乐笔记。

    五、Finally

    何以故。
    是诸众生。
    若心取相。即为着我人众生寿者。
    若取法相。即着我人众生寿者。

    展开全文
  • Android音频焦点

    2019-06-26 15:41:08
    AudioManager mAudioManager = (AudioManager)getSystemService(AUDIO_SERVICE); //获得音频焦点 //第三个参数:告知系统,你...//AUDIOFOCUS_GAIN_TRANSIENT:只是短暂获得,一会就释放焦点,比如你只是想发个...

    AudioManager mAudioManager = (AudioManager)getSystemService(AUDIO_SERVICE);

    //获得音频焦点
    //第三个参数:告知系统,你要求获得音频焦点的用途,系统根据你所要求的类型来给其他监听者发出相应的焦点控制参数。有三种可选
    //AUDIOFOCUS_GAIN_TRANSIENT:只是短暂获得,一会就释放焦点,比如你只是想发个notification时用下一秒不到的铃声。
    //AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK:只是背景获得,之前的音频焦点使用者无需释放焦点给我,我将与其共同使用。
    //AUDIOFOCUS_GAIN:我要求完全获得焦点,其他人需要释放焦点。比如我要播放音乐了,这时就要抢占整个音频焦点。
    //AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE:短暂性获得焦点,录音或者语音识别。


    mAudioManager.requestAudioFocus(mAudioFocusListener,AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);

    //focusChange主要有以下四种参数:
    //AUDIOFOCUS_AGIN:你已经完全获得了音频焦点
    //AUDIOFOCUS_LOSS:你会长时间的失去焦点,所以不要指望在短时间内能获得。请结束自己的相关音频工作并做好收尾工作。比如另外一个音乐播放器开始播放音乐了(前提是这个另外的音乐播放器他也实现了音频焦点的控制,baidu音乐,天天静听很遗憾的就没有实现,所以他们两个是可以跟别的播放器同时播放的)
    //AUDIOFOCUS_LOSS_TRANSIENT:你会短暂的失去音频焦点,你可以暂停音乐,但不要释放资源,因为你一会就可以夺回焦点并继续使用
    //AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:你的焦点会短暂失去,但是你可以与新的使用者共同使用音频焦点

    private OnAudioFocusChangeListener mAudioFocusListener = new OnAudioFocusChangeListener(){
            public void onAudioFocusChange(int focusChange) {
                switch(focusChange){
                    case AudioManager.AUDIOFOCUS_LOSS:
                        if(isPlaying()){
                          //we do not need get focus back in this situation
                          //会长时间失去,所以告知下面的判断,获得焦点后不要自动播放
                            mPausedByTransientLossOfFocus = false;
                            pause();//因为会长时间失去,所以直接暂停
                        }
                        break;
                    case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
                    case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
                        if(isPlaying()){
                           //短暂失去焦点,先暂停。同时将标志位置成重新获得焦点后就开始播放
                            mPausedByTransientLossOfFocus = true;
                            pause();
                        }
                        break;
                    case AudioManager.AUDIOFOCUS_GAIN:
                        //重新获得焦点,且符合播放条件,开始播放
                        if(!isPlaying()&&mPausedByTransientLossOfFocus){
                            mPausedByTransientLossOfFocus = false;
                            resume();
                        }
                        break;
                }
            }};

    //释放音频焦点
    mAudioManager.abandonAudioFocus(mAudioFocusListener);

    展开全文
  • Android 音频焦点处理

    2019-08-12 15:57:20
    然而对比同类产品,发现同类产品可以放到播放自如,体验很好,通过对比研究,根源就在于音频焦点处理上。 一、引言 在功能机时代,系统是不允许有两个或多个音源播出,因为有优先级管理,但在智能机系统中,多个...

    刚开始的时候,认为在智能机中,每个 APP 都是各自管各自的,媒体播放也是这样子的;然而对比同类产品,发现同类产品可以放到播放自如,体验很好,通过对比研究,根源就在于音频焦点处理上。

    一、引言

    在功能机时代,系统是不允许有两个或多个音源播出,因为有优先级管理,但在智能机系统中,多个音源同时播放是被允许的,两个或更多的 Android 应用程序可以同时播放音频到相同的输出流。系统把所有东西混合在一起。虽然这在技术上是令人印象深刻的,但对用户来说却是非常令人恼火的,体验感极差。为了避免所有音乐应用同时播放,Android 引入了音频聚焦的概念。只有一个应用程序可以一次聚焦音频。

    当应用程序需要输出音频时,它应该请求音频焦点。当它有焦点时,它可以播放声音。然而,在获得音频焦点后,可能无法持有它直到你播放完。另一个应用程序可以请求焦点,它会抢占你的音频焦点。如果出现这种情况,你的应用程序应该暂停播放或降低音量,让用户更容易听到新的音频源。

    音频聚焦是合作的。鼓励应用程序遵守音频聚焦指南,但该系统不执行这些规则。如果一个应用程序想在失去声音焦点后继续大声播放,没有什么能阻止它。这是一种糟糕的体验,用户很有可能会卸载出现这种糟糕体验的应用程序。

    二、音频焦点申请与释放

    一个表现良好的音频应用程序应该管理音频焦点根据这些一般准则:

    • 在开始播放之前立即调用 requestAudioFocus(),并验证调用是否返回 AUDIOFOCUS_REQUEST_GRANTED。如果按照参考设计中描述的那样设计应用程序,那么应该在媒体会话的 onPlay() 回调中调用 requestAudioFocus()。
    • 当另一个应用程序获得音频焦点时,停止或暂停播放,或降低音量。
    • 当播放停止,放弃音频焦点。

    音频焦点的处理方式因 Android 版本不同而异:

    • 从 Android 2.2 (API level 8) 开始,应用程序通过调用 requestAudioFocus() 和 abandonAudioFocus() 来管理音频焦点。应用程序还必须注册一个 AudioManager.OnAudioFocusChangeListener,以接收回调和管理自己的音频级别。
    • 对于 Android 5.0 (API level 21) 或更高版本的应用程序,音频应用程序应该使用 AudioAttributes 来描述你的应用程序正在播放的音频类型。例如,播放语音的应用程序应该指定CONTENT_TYPE_SPEECH。
    • 运行 Android 8.0 (API级别26) 或更高版本的应用程序应该使用 requestAudioFocus() 方法,它接受 AudioFocusRequest 参数。AudioFocusRequest 包含关于音频上下文和应用程序功能的信息。系统使用这些信息自动管理音频焦点的获取和丢失。

    三、音频聚焦

    当你调用 requestAudioFocus() 时,你必须指定一个 duration hint,这个 duration hint 可能会被另一个当前保持焦点并播放的应用程序使用:

    • 当你计划在可预见的将来播放音频时(例如,播放音乐时)请求永久的音频焦点(AUDIOFOCUS_GAIN),并且您希望先前的音频焦点持有者停止播放。

    • 请求瞬态焦点(AUDIOFOCUS_GAIN_TRANSIENT),当您希望只在短时间内播放音频,而您希望之前的持有者暂停播放。

    • 请求 ducking 瞬态焦点 (AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK),以指示您希望只在短时间内播放音频,并且如果先前的焦点所有者 “duck”(降低) 其音频输出,仍可以继续播放着。两个音频输出都混合到音频流中。Ducking 特别适用于间歇性使用音频流的应用程序,比如声音驱动方向。

    requestAudioFocus() 方法也需要一个 AudioManager.OnAudioFocusChangeListener。这个侦听器应该创建在你的媒体会话所处的相同活动或服务中。它实现了回调 onAudioFocusChange(),当其他应用程序获得或放弃音频焦点时,您的应用程序会接收到这个回调。

    以下代码片段请求流 STREAM_MUSIC 上的永久音频焦点,并注册一个 OnAudioFocusChangeListener 来处理音频焦点:

       public static AudioFocusUtils getInstance() {
            if (mIntance == null) {
                mIntance = new AudioFocusUtils();
                mAudioManager = (AudioManager) CGenieApplication.getInstance().getApplicationContext().getSystemService(Context.AUDIO_SERVICE);
            }
            return mIntance;
        }
    
        private AudioManager.OnAudioFocusChangeListener mOnAudioFocusChangeListener = new
                AudioManager.OnAudioFocusChangeListener() {
    
                    @Override
                    public void onAudioFocusChange(int focusChange) {
                        adjustAudioFocus(focusChange);
                    }
                };
    
        public void adjustAudioManagerListener(boolean isRequestFocus) {
            if (mAudioManager == null) {
                return;
            }
            if (isRequestFocus) {
                mAudioManager.requestAudioFocus(mOnAudioFocusChangeListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
            } else {
                mAudioManager.abandonAudioFocus(mOnAudioFocusChangeListener);
            }
        }
    
        private void adjustAudioFocus(int focusChange) {
            logShow("focusChange: " + focusChange);
            switch (focusChange) {
                case AudioManager.AUDIOFOCUS_LOSS:
                case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
                case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
                    mAudioFocusLossState = true;
                    MediaManager.getInstance().suspendAudioPlaying();   //失去焦点,暂停播放
                    break;
                case AudioManager.AUDIOFOCUS_GAIN:
                    if (mAudioFocusLossState) {
                        mAudioFocusLossState = false;
                        MediaManager.getInstance().resumeAudioPlaying();   //获得焦点,恢复播放
                    }
                    break;
                default:
                    break;
            }
        }
    
    

    当前,这种处理方式,在 Android5.0 – Android 9.0 上验证,都是有效的。

    四、对音频焦点变化的响应

    当一个应用程序获得音频焦点时,它必须能够在另一个应用程序请求自己的音频焦点时释放它。当发生这种情况时,应用程序在 AudioFocusChangeListener 中接收到对onAudioFocusChange() 方法的调用,该调用是在调用 requestAudioFocus() 时指定的。

    传递给 onAudioFocusChange() 的 focusChange 参数指示正在发生的变化。它对应于应用程序获取焦点所使用的持续时间提示。你的应用程序应该做出适当的响应。

    瞬态失焦

    如果焦点的变化是瞬态的(AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK或AUDIOFOCUS_LOSS_TRANSIENT),应用程序应该 ducking 降低音量(如果你不依赖自动 ducking)或暂停播放,但以其他方式保持相同的状态。
    在音频焦点暂时消失期间,您应该继续监视音频焦点的变化,并准备在恢复焦点后恢复正常回放。当阻塞应用程序放弃焦点时,您将收到一个回调(AUDIOFOCUS_GAIN)。此时,您可以将音量恢复到正常水平或重新开始播放。

    永久失去焦点

    如果音频焦点丢失是永久性的(AUDIOFOCUS_LOSS),另一个应用程序正在播放音频。你的应用应该立即暂停播放,因为它永远不会收到 AUDIOFOCUS_GAIN 回调。要重新启动回放,用户必须采取显式操作,比如在通知或应用程序UI中按下 play 传输控件。


    补充:

    音频 APP 可以设置两种播放模式一种可以被抢占式(音乐 APP 主流模式),一种不被抢占式(但是会和其他混合播放)
    先打开第三方 APP 并播放音乐,再打开我们 APP 及操作音频播放,第三方APP设置的是同时播放,两边都会播放;第三方设置的是可以被抢占式的,那我们 APP 能播放,第三方 APP 就播放不了(会暂停)。
    同理,先打开我们 APP 及操作音频播放,再打开第三方 APP 并播放音乐,我们的 APP 一般都设置为可以被抢占式的,就会暂停 APP 音频播放。

    展开全文
  • Android音频焦点处理

    2018-12-07 17:20:14
    概要 Android系统允许多个应用同时播放音频,这种特性有利有弊。例如当我们正在听音乐的时候突然点开了一个视频,如果我们发现音乐的声音和视频的的声音混合了在一起,这显然让我们非常不...Android引入音频焦点(aud...

    概要

    Android系统允许多个应用同时播放音频,这种特性有利有弊。例如当我们正在听音乐的时候突然点开了一个视频,如果我们发现音乐的声音和视频的的声音混合了在一起,这显然让我们非常不爽。而如果我们在播放音乐的时候,突然来了一条信息,这个时候,我们既希望能听到短信的提示音,又不希望音乐此刻停止,而是希望音乐可以降低音量播放以使我们能听清楚短信提示音,之后再恢复音量。Android引入音频焦点(audio focus)这一特性,可以让我们方便管理音频焦点,从而让app可以应对各种情况。

    音频焦点具有协调型,也就是说在同一时刻,只有一个app可以获取音频焦点。当我们在开发一款Music应用时,如果我们想播放一首歌曲,最好先获取音频焦点,但是这并不是系统强制行为,然而如果我们不这样做,这可能就是个糟糕的应用,用户基本上都会卸载它。

    如果我们获取了焦点,但是这并不意味着你能一直拥有它,其他应用也此时也可以请求焦点,那么你的应用可能会失去焦点,那么我们就应该做出相应的处理,例如暂停播放音乐,等到焦点再次获取焦点后,继续播放音乐。

    设计原则

    前面我们提到,如果你的App表现的很糟糕,用户就会卸载你的App,那么对于音频焦点的处理流程,大致可以归纳为一下几点:

    1. 在使用音频前(例如播放音乐),需要先获取焦点,如果成功获取到焦点,才应该正式开始工作。
    2. 当其他app请求了焦点,而此时我们的app失去焦点,我们需要做相应的处理。例如,如果是短暂型失去焦点,我们可以暂停工作,如果是永久性失去焦点,我们就停止工作。
    3. 当完成工作后,我们需要手动释放音频焦点。

    版本兼容

    从Android 8.0(O版本,API 26)开始,音频焦点的请求方式以及系统管理有了细微的变化,下面我分两部分来说明。

    8.0 之前的实现

    当你想要录音或者播放一首歌曲的时候,最好(注意是最好,不是强制)先请求音频焦点,这个时候你需要调用AudioManager.requestAudioFocus()方法,函数原型如下

    public int requestAudioFocus(OnAudioFocusChangeListener l, int streamType, int durationHint)
    

    第一个参数用于监听焦点的变化,例如你的app在工作期间可能失去焦点。

    第二个参数表明请求的音频焦点影响的是那种类型流,例如,如果我们录音,我们肯定是要影响Music这一类型的音频流,因此可以选择AudioManager.STREAM_MUSIC。当然还有许多类型,例如与铃声有关的AudioManager.STREAM_RING,与通话有关的是AudioManager.STREAM_VOICE_CALL

    第三个参数用于表明音频焦点的持续时间,这个很关键,它也有许多种类型,下面一一列出。

    1. AudioManager.AUDIOFOCUS_GAIN: API文档说请求的这类音频焦点持续时间是未知的,通常用来表示长时间获取焦点,可以用来播放音乐,录音等等。
    2. AudioManager.AUDIOFOCUS_GAIN_TRANSIENT: 表明请求的音频焦点持续时间比较短,通常用来播放导航路线的声音,或者播放通知声音。
    3. AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK: 这个也是表明请求的音频焦点持续时间比较短,但是在这段时间内,它希望其他应用以较低音量继续播放。例如,我们在使用导航的时候可以听音乐,当出现导航语音的时候,音乐音量会降低以便我们能听清楚导航的语音,当导航语音播放完毕后,音乐恢复音量,继续播放。
    4. AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE: 这个也是表明音请求的音频焦点持续时间比较短,但是在这段时间内,不希望任何应用(包括系统应用)来做任何与音频相关的事情,就算是降低音量播放音乐也是不被希望的。例如当我们进行录音或者语音识别的时候,我们不希望其他的声音出现干扰。

    AudioManager.requestAudioFocus()的返回值表明请求的结果,AudioManager.AUDIOFOCUS_REQUEST_FAILED表明请求焦点失败,AudioManager.AUDIOFOCUS_REQUEST_GRANTED表明请求焦点成功。

    当我们成功请求焦点后,就可以做一些与音频有关的事情,例如播放音乐,录音,或者语音识别。当完成这些工作后,我们必须调用AudioManager.abandonAudioFocus(onAudioFocusChangeListener l)释放音频焦点。

    8.0 之后实现

    从Android 8.0开始(API 26),请求音频焦点的方式以及系统对音频焦点变化的管理有些微妙的变化。

    首先,对音频焦点变化的管理的变化体现在两个方面,延迟获取焦点和自动降低音量。

    音频焦点变化管理

    延迟获取焦点


    在Android 8.0之前,当我们请求音频焦点的时候,只会返回两种结果,要么请求成功(AUDIOFOCUS_REQUEST_GRANTED),要么请求失败(AUDIOFOCUS_REQUEST_FAILED)。

    而从Android 8.0开始,还有一种结果,延迟成功请求(AUDIOFOCUS_REQUEST_DELAYED),这个也是成功的请求,但是这个请求具有延迟性。例如当我们处于通话状态的时候,我们很显然不希望任何app来获取到音频焦点来做些事,例如播放音乐。

    然而只有设置过AudioFocusRequest.Builder.setAcceptsDelayedFocusGain(true)才能获取到这种结果,这个我们后面会讲到。那么我们怎么知道什么时候获取到了音频焦点呢,当然还需要设置AudioManager.OnAudioFocusChangeListener这个音频焦点变化的监听器,通过回调确认何时获取到了音频焦点。

    自动降低音量


    在Android 8.0之前,如果请求焦点使用了AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK参数,它表明希望拥有了音频焦点的其他应用降低音量来使用音频,然而并不是所有的应用都会这样做(有可能开发者忘记了优化),因为这并不是系统强制的。But, 从Android 8.0开始,这个降低音量的工作,就是系统默认行为了,可以说是一个良心的优化。

    如果我不希望系统自动给我降低音量,而是想自己暂停音频相关的工作,那咋办?这个可以通过AudioFocusRequest.Builder.setWillPauseWhenDucked(true)方法取消系统的默认行为,然后通过监听音频焦点变化,来自己处理,这个我们后面也会讲到。

    音频焦点请求方式

    请求和释放音频焦点的方法名没有改变,都是用AudioManager.requestAudioFocus()请求焦点,用AudioManager.releaseAudioFocus()来释放焦点,但是这两个方法在Android 8.0后需要传入一个AudioFocusRequest对象,这个对象的构造方式使用的是Builder模式来设置参数,下面看下一些设置参数的方法:

    1. setFocusGain(): 只有这个方法是必须的,而传入的参数与8.0之前使用AudioManager.requestAudioFocus()传入的第三个参数一样,都是用来表示持续时间。
    2. setAudioAttributes(): 这个方法是用来描述app的使用情况。这方法需要传入一个AudioAttributes对象,这个对象也是使用Builder模式来构造,例如使用AudioAttributes.Builder.setUsage()来描述使用这个音频来干什么,我们可以传入一个AudioAttributes.USAGE_MEDIA来表明用这个音频来作为媒体文件来播放,也可以传入一个AudioAttributes.USAGE_ALARM来表明用这个来作为闹铃。
    3. setWillPauseWhenDucked(): 这个前面说过,是为了覆盖系统默认降低音量的行为,但是必须要设置AudioManager.OnAudioFocusChangeListener才能自己处理这类情况。
    4. setAcceptsDelayedFocusGain(): 这个前面也说过,这个是为了能够延迟获取到焦点的必须条件,但是同时也必须要设置AudioManager.OnAudioFocusChangeListener才能得知何时获取到焦点。
    5. setOnAudioFocusChangeListener(): 音频焦点变化监听器。值得一提的是这个方法有个重载的方法,有一个重载方法有两个参数,第二个参数为Handler对象,看到Handler应该明白了,是为了使用它的消息队列来顺序处理这个回调。

    监听音频焦点的变化

    在请求音频焦点的时候,我们需要监听焦点的变化,以便做出正确的行为,我们需要设置并实现OnAudioFocusChangeListener接口。

        public interface OnAudioFocusChangeListener {
            public void onAudioFocusChange(int focusChange);
        }
    

    根据参数int focusChange的值,我们需要做出合适的行为,而这些值大致可以分为两种类型,获取焦点和失去焦点。获取焦点比较简单,它的值是AudioManager.AUDIOFOCUS_GAIN。而失去焦点又分为两种类型,一种是短暂失去焦点,另一种是永久失去焦点。

    短暂失去焦点


    这种类型包括AudioManager.AUDIOFOCUS_LOSS_TRANSIENTAudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK。如果是AudioManager.AUDIOFOCUS_LOSS_TRANSIENT,我们应该暂停音乐的播放,而如果是AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK,我们应该降低音量继续播放,当然根据工作内容的不同,相应的处理也会有所不同。

    如果我们还想恢复状态继续工作,那么在短暂失去音频焦点后,应该持续监听音频焦点的变化,当我们再次获取焦点的时候,就恢复正常的工作状态,例如调整音量播放以你月,或者恢复播放暂停的音乐。就如同我们前面举个导航和音乐的例子,当有导航声音的时候,音乐需要降低音量播放,当导航声音播放完毕,音乐需要恢复音量。

    永久失去焦点


    这种类型是指AudioManager.AUDIOFOCUS_LOSS,当遇到这种功能情况,我们需要立即暂停,甚至有时候需要停止正在进行的工作,因为我们将不会再获取到焦点。那么为了恢复正常工作,需要用户去手动做这些事。例如,当我们正在听音乐,我们此时打开了一个视频App,这个时候音乐App会失去音频焦点并暂停播放,当我们关闭视频后,音乐App并不会再次获取到音频焦点,因此需要用户手动去点击播放。

    代码实现

    我在项目中处理录音的时候,需要去请求焦点,于是我做了一个简单的封装,代码如下

    public class AudioFocusManager implements AudioManager.OnAudioFocusChangeListener {
        private AudioManager mAudioManager;
        private AudioFocusRequest mFocusRequest;
        private AudioAttributes mAudioAttributes;
    
        private onRequestFocusResultListener mOnRequestFocusResultListener;
        private OnAudioFocusChangeListener mAudioFocusChangeListener;
    
        public AudioFocusManager(Context context) {
            mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
        }
    
        /**
         * Request audio focus.
         */
        public void requestFocus() {
            int result;
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                if (mFocusRequest == null) {
                    if (mAudioAttributes == null) {
                        mAudioAttributes = new AudioAttributes.Builder()
                                .setUsage(AudioAttributes.USAGE_MEDIA)
                                .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
                                .build();
                    }
                    mFocusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
                            .setAudioAttributes(mAudioAttributes)
                            .setWillPauseWhenDucked(true)
                            .setOnAudioFocusChangeListener(this)
                            .build();
                }
                result = mAudioManager.requestAudioFocus(mFocusRequest);
            } else {
                result = mAudioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
            }
            if (mOnRequestFocusResultListener != null) {
                mOnRequestFocusResultListener.onHandleResult(result);
            }
        }
    
    
        @Override
        public void onAudioFocusChange(int focusChange) {
            if (mAudioFocusChangeListener != null) {
                mAudioFocusChangeListener.onAudioFocusChange(focusChange);
            }
        }
    
        /**
         * Release audio focus.
         */
        public void releaseAudioFocus() {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                mAudioManager.abandonAudioFocusRequest(mFocusRequest);
            } else {
                mAudioManager.abandonAudioFocus(this);
            }
        }
    
        /**
         * Handle the result of audio focus.
         */
        public interface onRequestFocusResultListener {
            void onHandleResult(int result);
        }
    
    
        public void setOnHandleResultListener(onRequestFocusResultListener listener) {
            mOnRequestFocusResultListener = listener;
        }
    
    
        /**
         * Same as AudioManager.OnAudioFocusChangeListener.
         */
        public interface OnAudioFocusChangeListener {
            void onAudioFocusChange(int focusChange);
        }
    
    
        public void setOnAudioFocusChangeListener(OnAudioFocusChangeListener listener) {
            mAudioFocusChangeListener = listener;
        }
    
    }
    

    如果你是为了播放音频,还需要做一些相应的改变,可以参考前面讲过的理论知识。

    参考

    https://developer.android.google.cn/guide/topics/media-apps/audio-focus

    展开全文
  • 今天我们先来看一下音频焦点的相关内容,分为上下篇,上篇主要介绍音频焦点在媒体类应用中的使用,下篇进行源码分析,我们学习源码不仅仅是为了更深入的了解,更主要的目的是学习Android的设计架构,为我们在平时...
  • 什么叫音频焦点 (audio focus)?  android系统是一个多任务操作系统,因此同一时刻允许许多任务同时工作。但是这对音频类应用来说是个挑战,因为如果多个音频同时播放的话,很多情况下用户体验会相当的差!...
  • Android通过AudioFocus机制对音频焦点进行管理。 Music通过下述代码申请Focus权限: AudioManager mAudioManager; mAudioManager.requestAudioFocus(mAudioFocusListener, AudioManager.STREAM_MUSIC,...
  • 两个或更多的Android应用程序可以同时播放音频到相同的输出流。系统把所有东西混合在一起。虽然这在技术上是令人印象深刻的,但对用户来说却是非常令人恼火的。为了避免所有音乐应用同时播放,Android引入了音频聚焦...
  • 问题发现 最近在项目中发现我们的自己的apk和第三方的媒体apk会出现串音问题。即两者之间的声音会重叠在一起。 ...1. 使用多媒体按键去控制第三方应用的声音播放(无效果) ...通过触发媒体的按钮来操作播放器状态:...
  • Android ViewPager 焦点图轮播目前很多App都用了焦点图,假如你喜欢玩LoL那么掌上英雄联盟绝对是你的最爱,假如你看新闻网易新闻客户端绝对是首选,他们首页中就运用了焦点图。废话不多说,来看看怎么实现焦点图轮播...
  • 因为系统中可能会有多个应用程序会播放音频,所以需要考虑他们之间该如何交互,为了避免多个应用程序同时播放音乐,Android 系统使用音频焦点来进行统一管理,即只有获得了音频焦点的应用程序才可以播放音乐。...
  • 音频焦点的基本用法

    2017-09-19 18:36:16
    请求焦点: /** * Request audio focus. * Send a request to obtain the audio focus * @param l the listener to be notified of audio focus changes * @param streamType the main audio st
  • android 音频焦点

    2019-08-15 08:37:58
    AUDIOFOCUS_GAIN_TRANSIENT:只是短暂获得,一会就释放焦点,比如你只是想发个notification时用下一秒不到的铃声。 AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK:只是背景获得,之前的音频焦点使用者无需释放焦点给我,我将...
1 2 3 4 5 ... 20
收藏数 1,580
精华内容 632
关键字:

短焦点释放android