草庐IT

实测Android音频的焦点获取和归还

静默加载 2023-04-21 原文

实测Android音频的焦点获取和归还

前言

最近老板想在产品中的短视频后者直播播放的时候对于手机中的音乐播放器进行暂停播放,并且退出视频播放后手机的音乐播放器还能继续播放之前的音乐。

先试试微信,emmm,确实可以。

Android官网:管理音频焦点

官网管理音频焦点准则:

  • 在即将开始播放之前调用 requestAudioFocus(),并验证调用是否返回 AUDIOFOCUS_REQUEST_GRANTED。如果按照本指南中的说明设计应用,则应在媒体会话的 onPlay() 回调中调用 requestAudioFocus()
  • 在其他应用获得音频焦点时,停止或暂停播放,或降低音量。
  • 播放停止后,放弃音频焦点。

不同版本音频焦点的处理方式不太相同:

  • 从 Android 2.2(API 级别 8)开始,应用通过调用 requestAudioFocus()abandonAudioFocus() 来管理音频焦点。应用还必须为这两个调用注册 AudioManager.OnAudioFocusChangeListener,以便接收回调并管理自己的音量。

  • 对于以 Android 5.0(API 级别 21)及更高版本为目标平台的应用,音频应用应使用 AudioAttributes 来描述应用正在播放的音频类型。例如,播放语音的应用应指定 CONTENT_TYPE_SPEECH

  • 面向 Android 8.0(API 级别 26)或更高版本的应用应使用 requestAudioFocus() 方法,该方法会接受 AudioFocusRequest 参数。AudioFocusRequest 包含有关应用的音频上下文和功能的信息。系统使用这些信息来自动管理音频焦点的得到和失去。

API介绍

处理音频焦点都是通过AudioManager这个类,如下是获得该类实例的方法:
AudioManager am = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);

requestAudioFocus()//用于申请音频焦点
abandonAudioFocus() //用于释放音频焦点
AudioManager.OnAudioFocusChangeListener 接口,提供了 onAudioFocusChange() 方法来监听音频焦点变化

  • requestAudioFocus(OnAudioFocusChangeListener l, int streamType, int durationHint)参数:

    • AudioManager.OnAudioFocusChangeListener l
      用于监听音频焦点变化,从而可以进行适当的操作,例如暂停播放等。

    • streamType
      申请音频焦点处理的音频类型,例如,当播放音乐时,可以传入 STREAM_MUSIC ;当播放铃声时,可以传入 STREAM_RING 。表中列出了一些的可选值:

      类型含义
      STREAM_VOICE_CALL通话0
      STREAM_SYSTEM系统1
      STREAM_RING铃声2
      STREAM_MUSIC音乐3
      STREAM_ALARM闹铃4
      STREAM_NOTIFICATION系统通知5
    • durationHint (PS:重要参数):
      可选值有以下五个:
      (1) AUDIOFOCUS_GAIN: 此参数表示希望申请一个永久的音频焦点,并且希望上一个持有音频焦点的App停止播放;例如在需要播放音乐时。
      (2) AUDIOFOCUS_GAIN_TRANSIENT:表示申请一个短暂的音频焦点,并且马上就会被释放,此时希望上一个持有音频焦点的App暂停播放。例如播放一个提醒声音。
      (3) AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK:效果同 AUDIOFOCUS_GAIN_TRANSIENT ,只是希望上一个持有焦点的App减小其播放声音(但仍可以播放),此时会混音播放。例如导航播报。
      (4) AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE: 表示申请一个短暂的音频焦点,并且会希望系统不要播放任何突然的声音(例如通知,提醒等),例如用户在录音。

    • 返回值:
      AUDIOFOCUS_REQUEST_GRANTED 或者 AUDIOFOCUS_REQUEST_FAILED

  • abandonAudioFocus(OnAudioFocusChangeListener l) 参数通上 AudioManager.OnAudioFocusChangeListener .

  • AudioManager.OnAudioFocusChangeListener :当音频焦点发生变化时进行 onAudioFocusChange(int focusChange) 方法的回调;

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;
    }
  }
};

实测代码:

核心是需要把 AudioManager.AUDIOFOCUS_GAIN 改为 AudioManager.AUDIOFOCUS_GAIN_TRANSIENT

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_TRANSIENT)
                    .setWillPauseWhenDucked(true)
                    .setAcceptsDelayedFocusGain(true)
                    .setOnAudioFocusChangeListener(mListener, mHandler)
                    .setAudioAttributes(mAttribute)
                    .build();
        }
    }
    private void requestAudioFocus() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
					mAudioManager.requestAudioFocus(mFocusRequest);
        } else {
          mAudioManager.requestAudioFocus(mListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
        }
    }
    private void abandonAudioFocus() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            mAudioManager.abandonAudioFocusRequest(mFocusRequest);
        } else {
          mAudioManager.abandonAudioFocus(mListener);
        }

    }
}

参考:

https://segmentfault.com/a/1190000022234509

https://www.jianshu.com/p/26ea60c499a7

文章到这里就全部讲述完啦,若有其他需要交流的可以留言哦~!~!

想阅读作者的更多文章,可以查看我 个人博客 和公共号:

有关实测Android音频的焦点获取和归还的更多相关文章

  1. ruby - 简单获取法拉第超时 - 2

    有没有办法在这个简单的get方法中添加超时选项?我正在使用法拉第3.3。Faraday.get(url)四处寻找,我只能先发起连接后应用超时选项,然后应用超时选项。或者有什么简单的方法?这就是我现在正在做的:conn=Faraday.newresponse=conn.getdo|req|req.urlurlreq.options.timeout=2#2secondsend 最佳答案 试试这个:conn=Faraday.newdo|conn|conn.options.timeout=20endresponse=conn.get(url

  2. ruby - 从 Ruby 中的主机名获取 IP 地址 - 2

    我有一个存储主机名的Ruby数组server_names。如果我打印出来,它看起来像这样:["hostname.abc.com","hostname2.abc.com","hostname3.abc.com"]相当标准。我想要做的是获取这些服务器的IP(可能将它们存储在另一个变量中)。看起来IPSocket类可以做到这一点,但我不确定如何使用IPSocket类遍历它。如果它只是尝试像这样打印出IP:server_names.eachdo|name|IPSocket::getaddress(name)pnameend它提示我没有提供服务器名称。这是语法问题还是我没有正确使用类?输出:ge

  3. ruby - 获取模块中定义的所有常量的值 - 2

    我想获取模块中定义的所有常量的值:moduleLettersA='apple'.freezeB='boy'.freezeendconstants给了我常量的名字:Letters.constants(false)#=>[:A,:B]如何获取它们的值的数组,即["apple","boy"]? 最佳答案 为了做到这一点,请使用mapLetters.constants(false).map&Letters.method(:const_get)这将返回["a","b"]第二种方式:Letters.constants(false).map{|c

  4. ruby-on-rails - 获取 inf-ruby 以使用 ruby​​ 版本管理器 (rvm) - 2

    我安装了ruby​​版本管理器,并将RVM安装的ruby​​实现设置为默认值,这样'哪个ruby'显示'~/.rvm/ruby-1.8.6-p383/bin/ruby'但是当我在emacs中打开inf-ruby缓冲区时,它使用安装在/usr/bin中的ruby​​。有没有办法让emacs像shell一样尊重ruby​​的路径?谢谢! 最佳答案 我创建了一个emacs扩展来将rvm集成到emacs中。如果您有兴趣,可以在这里获取:http://github.com/senny/rvm.el

  5. Ruby 从大范围中获取第 n 个项目 - 2

    假设我有这个范围:("aaaaa".."zzzzz")如何在不事先/每次生成整个项目的情况下从范围中获取第N个项目? 最佳答案 一种快速简便的方法:("aaaaa".."zzzzz").first(42).last#==>"aaabp"如果出于某种原因你不得不一遍又一遍地这样做,或者如果你需要避免为前N个元素构建中间数组,你可以这样写:moduleEnumerabledefskip(n)returnto_enum:skip,nunlessblock_given?each_with_indexdo|item,index|yieldit

  6. ruby - Net::HTTP 获取源代码和状态 - 2

    我目前正在使用以下方法获取页面的源代码:Net::HTTP.get(URI.parse(page.url))我还想获取HTTP状态,而无需发出第二个请求。有没有办法用另一种方法做到这一点?我一直在查看文档,但似乎找不到我要找的东西。 最佳答案 在我看来,除非您需要一些真正的低级访问或控制,否则最好使用Ruby的内置Open::URI模块:require'open-uri'io=open('http://www.example.org/')#=>#body=io.read[0,50]#=>"["200","OK"]io.base_ur

  7. ruby - 没有类方法获取 Ruby 类名 - 2

    如何在Ruby中获取BasicObject实例的类名?例如,假设我有这个:classMyObjectSystem我怎样才能使这段代码成功?编辑:我发现Object的实例方法class被定义为returnrb_class_real(CLASS_OF(obj));。有什么方法可以从Ruby中使用它? 最佳答案 我花了一些时间研究irb并想出了这个:classBasicObjectdefclassklass=class这将为任何从BasicObject继承的对象提供一个#class您可以调用的方法。编辑评论中要求的进一步解释:假设你有对象

  8. ruby-on-rails - 如何在 Gem 中获取 Rails 应用程序的根目录 - 2

    是否可以在应用程序中包含的gem代码中知道应用程序的Rails文件系统根目录?这是gem来源的示例:moduleMyGemdefself.included(base)putsRails.root#returnnilendendActionController::Base.send:include,MyGem谢谢,抱歉我的英语不好 最佳答案 我发现解决类似问题的解决方案是使用railtie初始化程序包含我的模块。所以,在你的/lib/mygem/railtie.rbmoduleMyGemclassRailtie使用此代码,您的模块将在

  9. ruby - 如何使用 CarrierWave 从 S3 获取真实文件 - 2

    我有一个应用程序可以读取文件的内容并为其编制索引。我将它们存储在磁盘本身中,但现在我使用的是AmazonS3,因此以下方法不再适用。事情是这样的:defperform(docId)@document=Document.find(docId)if@document.file?#Youshould'tcreateanewversion@document.versionlessdo|doc|@document.file_content=Cloudoc::Extractor.new.extract(@document.file.file)@document.saveendendend@docu

  10. 【鸿蒙应用开发系列】- 获取系统设备信息以及版本API兼容调用方式 - 2

    在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList​()Obt

随机推荐