当你的应用需要输出音频时,需要请求获得音频焦点,获得焦点后,就可以播放声音了。当其他应用请求焦点时,会抢占你持有的音频焦点,此时,你的应用应暂停播放或降低音量,以便于用户听到新的音频源。
注:音频焦点策略是Android的规范化要求,但是并不是Audio出声的必要条件。
java
代码解读
复制代码
AudioManager mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); int focusRequest = mAudioManager.requestAudioFocus( audioFocusListener, // 音频焦点监听器 AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN); switch (focusRequest) { case AudioManager.AUDIOFOCUS_REQUEST_FAILED: // 请求焦点失败 -> 不允许播放 break; case AudioManager.AUDIOFOCUS_REQUEST_GRANTED: // 请求焦点成功 -> 开始播放 break; }
整理了这份Java面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafka 面试专题
需要全套面试笔记【点击此处】即可免费获取
scss
代码解读
复制代码
val playbackAttributes = AudioAttributes.Builder() .setUsage(AudioAttributes.USAGE_MEDIA) .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) .build() mFocusRequest = AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN) .setAudioAttributes(playbackAttributes) .setAcceptsDelayedFocusGain(true) // 是否支持延时获取焦点 .setOnAudioFocusChangeListener(audioFocusListener, Handler(Looper.getMainLooper())) .build() // request接口需要用到mFocusRequest作为参数,mFocusRequest可一开始初始化一次 val focusRequest = mAudioManager.requestAudioFocus(mFocusRequest) if (focusRequest == AudioManager.AUDIOFOCUS_REQUEST_DELAYED) { // 申请delay了,需要处理焦点随后分发来的播放 } else if (focusRequest == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { // 申请成功,可以播放 } else if(focusRequest == AudioManager.AUDIOFOCUS_REQUEST_FAILED) { // 申请失败,不能播放 }
音频焦点的意图类型:
requestResult — AudioManager.AUDIOFOCUS_REQUEST_DELAYED 说明:
假如当用户在通话中打开游戏,他们想玩游戏,但是当前还在打电话所以不想听到游戏声音。但是当他们通话结束的时候他们想听到游戏声音。如果这个应用支持延迟音频聚焦,会发生如下情况:
目前低于 Android O 的版本是不支持延迟音频焦点这个功能的。
一旦音频聚焦改变,应用要马上做出响应,它的状态可能在任何时间发生改变(丢失或重新获取),我们可以实现 OnAudioFocusChangeListener 的来响应状态改变:
kotlin
代码解读
复制代码
val audioFocusListener = object : AudioManager.OnAudioFocusChangeListener { override fun onAudioFocusChange(focusChange: Int) { Timber.i("$TAG, onAudioFocusChange, state: $focusChange") when (focusChange) { AudioManager.AUDIOFOCUS_GAIN -> { handleAudioGain() } AudioManager.AUDIOFOCUS_LOSS -> { handleAudioLoss() } AudioManager.AUDIOFOCUS_LOSS_TRANSIENT -> { handleAudioLoss(true) } AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK -> {} // ignore else -> {} } } }
状态说明:
kotlin
代码解读
复制代码
val result = mAudioManager.abandonAudioFocusRequest(mFocusRequest) // 0: 失败, 1: 成功 Timber.i("$TAG, abandonAudioFocus end, result: $result")
关键字:QQAudioFocus(自己应用的音频的TAG)、MediaFocusControl、CarAudioFocus(车载)
结论: 车载媒体中心的音源抢占和通知只是决定当前系统关联模块的媒体信息展示,并不能作为各个媒体源的播放暂停控制依据。
成功
其他
失败
用户播放意图
尝试申请
Android焦点
requestAudioFocus
获取AudioFocus
结束
Psd
Csd
歌词服务
...
可以通过以下代码来对AudioFocus进行管理:
Kotlin
代码解读
复制代码
package com.max.audiofocus import android.content.Context import android.media.AudioAttributes import android.media.AudioFocusRequest import android.media.AudioManager import android.os.Handler import android.os.Looper import timber.log.Timber /** * @author MaChao * @Description 音频焦点管理单例类 */ object AudioFocusManager { private const val TAG = "AudioFocusManager" private val mAudioManager: AudioManager = (MusicApplication.mApp.getSystemService(Context.AUDIO_SERVICE)) as AudioManager private val mFocusRequest: AudioFocusRequest private var isHasFocus = false private var isPauseByLoss = false init { val audioFocusListener = AudioManager.OnAudioFocusChangeListener { focusChange -> Timber.i("$TAG, onAudioFocusChange, state: $focusChange") when (focusChange) { AudioManager.AUDIOFOCUS_GAIN -> { handleAudioGain() } AudioManager.AUDIOFOCUS_LOSS -> { handleAudioLoss() } AudioManager.AUDIOFOCUS_LOSS_TRANSIENT -> { handleAudioLoss(true) } AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK -> {} // ignore else -> {} } } val playbackAttributes = AudioAttributes.Builder() .setUsage(AudioAttributes.USAGE_MEDIA) .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) .build() mFocusRequest = AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN) .setAudioAttributes(playbackAttributes) .setAcceptsDelayedFocusGain(true) // 是否支持延时获取焦点 .setOnAudioFocusChangeListener(audioFocusListener, Handler(Looper.getMainLooper())) .build() } /** * 申请音频焦点 * @param forceRequest Boolean 是否强制调用AudioService申请(系统音频经常挂,导致异常焦点Loss了不会分配过来) * @return Boolean */ fun requestAudioFocus(forceRequest: Boolean = true): Boolean { if (!forceRequest && isHasFocus) { Timber.i("$TAG, requestAudioFocus, qqmusic already has Focus") isPauseByLoss = false return true } val result = mAudioManager.requestAudioFocus(mFocusRequest) // 0: 失败, 1: 成功, 2: delay Timber.i("$TAG, requestAudioFocus end, result: $result") if (result == AudioManager.AUDIOFOCUS_REQUEST_DELAYED) { // 申请delay了一会会通过audioListener分过来 isPauseByLoss = true } else if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { isPauseByLoss = false } isHasFocus = result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED return isHasFocus } /** * 当前是否已经有焦点 * @return Boolean */ fun isCurrHasFocus(): Boolean { return isHasFocus } /** * 抛弃音频焦点 * @return Boolean 是否抛成功 */ fun abandonAudioFocus(): Boolean { val result = mAudioManager.abandonAudioFocusRequest(mFocusRequest) // 0: 失败, 1: 成功 Timber.i("$TAG, abandonAudioFocus end, result: $result") isHasFocus = false isPauseByLoss = false return result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED } /** * 处理焦点获取 */ private fun handleAudioGain() { isHasFocus = true if (isPauseByLoss) { PlayManager.resume() } isPauseByLoss = false } /** * 处理焦点丢失 * @param isShortLoss Boolean 是否是短暂丢失 */ private fun handleAudioLoss(isShortLoss: Boolean = false) { isHasFocus = false if (PlayManager.isPlaying()) { PlayManager.pause() isPauseByLoss = true } if (!isShortLoss) { // 如果是长丢失,抛掉当前焦点 abandonAudioFocus() } } }