> 技术文档 > 音频焦点 Android Audio Focus_android 音频焦点

音频焦点 Android Audio Focus_android 音频焦点


Android 音频焦点详解

音频焦点(Audio Focus)是 Android 系统用于协调多个应用同时访问音频输出的机制。当多个应用需要播放音频时,音频焦点确保用户听到的内容不会混乱(如多个音乐应用同时播放)。

一、音频焦点的核心概念

  1. 音频焦点的类型

    • 永久性焦点:长时间占用焦点(如音乐播放器)。
    • 短暂性焦点:临时占用焦点(如导航提示音)。
    • Ducking:短暂降低其他应用音量(如通知音)。
  2. 焦点请求类型

    • AUDIOFOCUS_GAIN:请求长期焦点,其他应用需停止播放。
    • AUDIOFOCUS_GAIN_TRANSIENT:短暂占用焦点,其他应用需暂停。
    • AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK:短暂占用焦点,其他应用降低音量(Ducking)。
    • AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE:短暂独占焦点(如语音录制)。
  3. requestAudioFocus 返回值​

    • AUDIOFOCUS_REQUEST_GRANTED:请求成功
    • AUDIOFOCUS_REQUEST_FAILED:请求失败
    • AUDIOFOCUS_REQUEST_DELAYED:焦点延迟授予(需设置 AUDIOFOCUS_FLAG_DELAY_OK)
  4. 焦点丢失处理
    当其他应用请求焦点时,当前应用需根据情况暂停播放、停止播放或降低音量。

二、代码实现与分析

1. 请求音频焦点

使用 AudioManager 请求焦点,并监听焦点变化。

2. 音频焦点变化返回值

在播放结束或应用暂停时释放焦点:
当焦点状态变化时,系统通过此回调通知应用:

  • AUDIOFOCUS_LOSS:永久失去焦点(应停止播放并释放资源)。
  • AUDIOFOCUS_LOSS_TRANSIENT:暂时失去焦点(应暂停播放)。
  • AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:暂时失去焦点,但可降低音量继续播放。
  • AUDIOFOCUS_GAIN:重新获得焦点(恢复播放)。
// 初始化 AudioManagerAudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);// 创建焦点变化监听器AudioManager.OnAudioFocusChangeListener afChangeListener = new AudioManager.OnAudioFocusChangeListener() { @Override public void onAudioFocusChange(int focusChange) { switch (focusChange) { case AudioManager.AUDIOFOCUS_GAIN: // 重新获得焦点,恢复播放 mediaPlayer.start(); mediaPlayer.setVolume(1.0f, 1.0f); break; case AudioManager.AUDIOFOCUS_LOSS: // 永久丢失焦点,停止播放并释放资源 mediaPlayer.stop(); audioManager.abandonAudioFocus(afChangeListener); break; case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: // 暂时丢失焦点,暂停播放 mediaPlayer.pause(); break; case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: // 短暂降低音量 mediaPlayer.setVolume(0.2f, 0.2f); break; } }};//requestAudioFocus//作用​:应用通过此方法请求音频焦点,表明它希望播放音频//参数://OnAudioFocusChangeListener:焦点变化时的回调监听器//streamType:音频流类型(如 STREAM_MUSIC)//durationHint:焦点持有类型(如 AUDIOFOCUS_GAIN 表示长期占用)//返回值​://AUDIOFOCUS_REQUEST_GRANTED:请求成功//AUDIOFOCUS_REQUEST_FAILED:请求失败//AUDIOFOCUS_REQUEST_DELAYED:焦点延迟授予(需设置 AUDIOFOCUS_FLAG_DELAY_OK)// 请求音频焦点(以 AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK 为例)int result = audioManager.requestAudioFocus( afChangeListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { // 焦点获取成功,开始播放 mediaPlayer.start();} else { // 焦点获取失败,处理逻辑}

音频焦点 Android Audio Focus_android 音频焦点

三、实际使用

一、通话打断音乐的流程
  1. 电话应用的优先级
    通话属于高优先级音频场景,系统会强制其他应用让出音频焦点。当来电时,电话应用会请求 AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE 类型的焦点(短暂独占),以确保通话音频的独占性。

  2. 音乐播放器的响应
    音乐播放器在失去焦点时,会通过注册的 OnAudioFocusChangeListener 收到 AUDIOFOCUS_LOSSAUDIOFOCUS_LOSS_TRANSIENT 回调,此时需暂停播放。

  3. 通话结束后的恢复
    当通话结束时,电话应用释放焦点,音乐播放器可能重新获得焦点(需主动重新请求),恢复播放。

四、framwork 跟 native

音频焦点 Android Audio Focus_android 音频焦点

1. AudioFocus 机制的关键角色
  • AudioManager:
    • 应用与 AudioFocus 系统交互的主要入口点。
    • 提供 requestAudioFocus() / abandonAudioFocus() / requestAudioFocusRequest() 等 API。
    • 持有 IAudioService 的代理(Binder 对象),用于跨进程调用 AudioService
  • AudioService:
    • 系统服务 (com.android.server.audio.AudioService),运行于 system_server 进程。
    • 管理整个系统的音频状态、策略、路由和焦点
    • 通过 Binder (IAudioService) 接收来自应用的请求。
    • 内部核心组件:MediaFocusControl - 实际管理焦点栈、处理请求和分发焦点变更通知的逻辑中心。
  • MediaFocusControl:
    • AudioService 的内部类,是 AudioFocus 机制的“大脑”。
    • 维护焦点栈 (mFocusStack): 一个 Stack,栈顶元素拥有当前音频焦点。栈大小有限制(MAX_STACK_SIZE=100)。
    • 处理请求: 接收 requestAudioFocus()abandonAudioFocus() 请求。
    • 分发通知: 当焦点状态变化时(如新应用获得焦点、原有应用丢失焦点),通知相应的应用。
    • 协调特殊状态: 如电话状态 (mRingOrCallActive)。
  • FocusRequester:
    • 表示一个音频焦点请求者。
    • 可参考代码
//创建新的焦点请求对象final FocusRequester nfr = new FocusRequester(aa, focusChangeHint, flags, fd, cb,  clientId, afdh, callingPackageName, uid, this, sdk, mEventLogger);
参数 含义 来源 aa 音频属性(如音乐/导航/通话),决定焦点优先级 请求方传入 focusChangeHint 焦点类型(如 AUDIOFOCUS_GAIN 永久占用,AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK 短暂降音) 请求方传入 [citation:6] flags 标志位(如 AUDIOFOCUS_FLAG_DELAY_OK 允许延迟获取) 请求方传入 fd 文件描述符(可能用于音频流控制) 系统分配 cb 客户端的 Binder 回调接口(用于焦点变更通知) 请求方传入 clientId 客户端唯一标识 系统生成 afdh 死亡回调处理器(监听客户端进程终止) 前文创建 [citation:6] callingPackageName 请求方包名 系统获取 uid 请求方用户 ID 系统获取 this 指向 MediaFocusControl 的引用(用于回调系统方法) 当前对象 sdk 请求方应用的 Target SDK 版本(影响兼容性行为) 系统获取 mEventLogger 事件日志器(记录焦点变更事件) 系统组件
  • IAudioFocusDispatcher:
    • 一个 AIDL 接口 (android.media.IAudioFocusDispatcher)。
    • 客户端实现 (AudioManager.mAudioFocusDispatcher): 一个 Stub 对象,运行在应用进程。当 MediaFocusControl 需要通知应用焦点变化时,通过 Binder 调用它的 dispatchAudioFocusChange(int focusChange, String clientId) 方法。
    • 服务端持有 (FocusRequester.mFocusDispatcher): MediaFocusControl 通过 FocusRequester 持有该接口的代理,指向客户端的 Stub
  • PlaybackActivityMonitor:
    • AudioService 的内部类,负责跟踪系统中所有活跃的音频播放器 (AudioTrackMediaPlayer)。
    • 维护播放器列表 (mPlayers): 一个 Map,key 是播放器 ID (piid),value 是播放器状态信息。
    • 强制执行策略: 在特定场景下(如电话呼入时),直接操作播放器实例(如调用 setVolume(0f) 静音)来实现系统级行为,即使应用没有正确处理焦点丢失。
  • AudioAttributes / StreamType:
    • AudioAttributes (推荐): Android 5.0 (API 21+) 引入的更强大的方式,描述音频的使用场景 (usage)、内容类型 (contentType)、标志等。更精确地指导音频路由和焦点行为。博客强调: 在 Android 9.0+ 的 requestAudioFocusRequest() 中,AudioAttributes 用于确定底层播放通路。
    • StreamType (旧方式): 传统的分类方式(如 STREAM_MUSIC, STREAM_RING)。在旧的 requestAudioFocus(listener, streamType, focusGain) 方法中使用。博客强调: 此方法中的 streamType 参数用于确定底层播放通路。
2. 焦点变更回调 (onAudioFocusChange)

当应用因他人请求焦点或自己释放焦点而导致其焦点状态改变时,通过注册的 OnAudioFocusChangeListener 会收到回调。参数 focusChange 表示具体的变更:

  • AUDIOFOCUS_GAIN 应用获得了(或重新获得了)焦点。应开始播放或恢复播放(如从暂停状态恢复,或提高之前被 Ducked 的音量)。
  • AUDIOFOCUS_LOSS 应用永久丢失了焦点。通常是因为另一个应用请求了 AUDIOFOCUS_GAIN。应立即停止播放并释放相关资源(如 MediaPlayer)。之后如果用户操作需要播放,应重新请求焦点。
  • AUDIOFOCUS_LOSS_TRANSIENT 应用暂时丢失了焦点。通常是因为另一个应用请求了 AUDIOFOCUS_GAIN_TRANSIENTAUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE。应暂停播放,但保留资源(不释放 MediaPlayer),准备在焦点回来时快速恢复。
  • AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK 应用暂时丢失了焦点,并被允许(或要求)降低音量(Ducking)。通常是因为另一个应用请求了 AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK。应将音量降低到一个较低水平(通常是原音量的 1/3 或更低)。在焦点回来时恢复原音量。
  • (较少见) AUDIOFOCUS_REQUEST_FAILED / AUDIOFOCUS_REQUEST_DELAYED: 请求焦点时返回的状态码,表示请求失败或被外部策略延迟,通常不会在 onAudioFocusChange 中收到。
3. 源码流程详解
  • 请求焦点 (requestAudioFocus / requestAudioFocusRequest):
    1. App -> AudioManager: App 调用 AudioManager 方法。
    2. AudioManager -> AudioService (Binder): AudioManager 通过 IAudioService Binder 调用 AudioService.requestAudioFocus()
    3. AudioService -> MediaFocusControl: AudioService 将请求委托给 MediaFocusControl.requestAudioFocus()
    4. MediaFocusControl 处理逻辑 (synchronized(mAudioFocusLock)):
      • 检查焦点栈大小: 超过 MAX_STACK_SIZE (100) 则失败返回。
      • 处理电话状态 (enteringRingOrCall): 检查是否为系统电话请求 (clientId == AudioSystem.IN_VOICE_COMM_FOCUS_ID),并设置 mRingOrCallActive
      • 注册死亡监听 (AudioFocusDeathHandler): 监听客户端 Binder 死亡,以便在其崩溃时清理焦点。
      • 移除同 clientId 的旧焦点: removeFocusStackEntry(clientId, false, false)关键点: 确保栈中同一 clientId 只存在一个焦点请求,避免栈溢出,提倡 OnAudioFocusChangeListener 复用。
      • 创建 FocusRequester (nfr): 封装请求信息。
      • 处理焦点栈顶变更: 如果栈非空(即有应用正在持有焦点),调用 propagateFocusLossFromGain_syncAf()。此方法遍历栈(主要是当前栈顶元素),调用其 handleFocusLossFromGain() -> handleFocusLoss()
      • 新焦点入栈: mFocusStack.push(nfr)
      • 通知新焦点获得者: nfr.handleFocusGainFromRequest(AUDIOFOCUS_REQUEST_GRANTED)
      • 强制执行电话静音: 如果是电话请求 (enteringRingOrCall),调用 runAudioCheckerForRingOrCallAsync(true) (延迟执行),最终在 PlaybackActivityMonitor.mutePlayersForCall() 中遍历 mPlayers,将匹配的播放器 (如 USAGE_MEDIA, USAGE_GAME) 音量设为 0 (apc.getPlayerProxy().setVolume(0.0f)),并记录在 mMutedPlayers 中。关键点: 这是系统级强制行为,独立于应用对焦点丢失的回调处理。
    5. FocusRequester 通知原焦点丢失者: 在步骤 4 的 handleFocusLoss() 中,通过 mFocusDispatcher.dispatchAudioFocusChange(focusLoss, clientId) (Binder 调用) 通知原焦点持有者的 AudioManager.mAudioFocusDispatcher
    6. AudioManager 回调 App: mAudioFocusDispatcher 收到调用后,根据请求时注册的 Handler (或默认的主线程 Handler),向该 Handler 发送消息。最终在该 Handler 的线程中,调用 App 注册的 OnAudioFocusChangeListener.onAudioFocusChange(focusLoss)关键点:Handler 的选择决定了回调线程,默认在主线程,可通过 AudioFocusRequest.Builder.setHandler() 指定工作线程 Handler 避免主线程阻塞。
  • 释放焦点 (abandonAudioFocus / abandonAudioFocusRequest):
    播放结束后调用
    1. App -> AudioManager: App 调用 AudioManager 方法。
    2. AudioManager -> AudioService (Binder): AudioManager 通过 IAudioService Binder 调用 AudioService.abandonAudioFocus()
    3. AudioService -> MediaFocusControl: AudioService 将请求委托给 MediaFocusControl.abandonAudioFocus()
    4. MediaFocusControl 处理逻辑 (synchronized(mAudioFocusLock)):
      • 处理退出电话状态 (exitingRingOrCall): 类似请求逻辑。
      • 移除焦点: removeFocusStackEntry(clientId, true, true)关键点: signal=true 表示移除后需要通知新的栈顶。
      • 解除电话静音: 如果是电话释放 (exitingRingOrCall),调用 runAudioCheckerForRingOrCallAsync(false),最终在 PlaybackActivityMonitor.unmutePlayersForCall() 中遍历 mMutedPlayers,恢复对应播放器的音量 (apc.getPlayerProxy().setVolume(1.0f)) 并清空列表。
    5. 通知新的栈顶获得者:removeFocusStackEntry() 中,如果 signal=true 且栈非空,调用 notifyTopOfAudioFocusStack() -> mFocusStack.peek().handleFocusGain(AUDIOFOCUS_GAIN) -> fd.dispatchAudioFocusChange(AUDIOFOCUS_GAIN, mClientId) (Binder 调用)。
    6. AudioManager 回调新焦点获得者: 过程同请求流程的第 6 步,最终调用新栈顶应用注册的 onAudioFocusChange(AUDIOFOCUS_GAIN)
  • 播放器跟踪 (AudioTrack / MediaPlayer 创建):
    1. 创建 AudioTrack / MediaPlayer 时,在构造函数中会调用父类 PlayerBase.baseRegisterPlayer()
    2. PlayerBase -> AudioService (Binder): 通过 IAudioService 调用 AudioService.trackPlayer()
    3. AudioService -> PlaybackActivityMonitor: AudioService 委托给 mPlaybackMonitor.trackPlayer()
    4. PlaybackActivityMonitor 记录播放器: 生成新的 piid,创建 AudioPlaybackConfiguration(apc) 对象,并将其放入 mPlayers Map (mPlayers.put(newPiid, apc))。关键点: 这使得系统能够全局跟踪所有活跃播放器,为强制执行策略(如电话静音)提供了基础。
4. 成员关系
  1. clientIdOnAudioFocusChangeListener 复用: clientIdgetIdForAudioFocusListener(listener) 生成。强烈建议复用同一个 OnAudioFocusChangeListener 实例。 原因:
    • 避免在栈中创建多个相同逻辑的焦点请求,浪费栈空间。
    • 焦点栈大小有限 (100),复用 listener 能确保同一个应用的多次请求只占一个栈位,防止栈满导致后续请求失败 (AUDIOFOCUS_REQUEST_FAILED)。
    • 便于统一管理焦点状态。
  2. Handler 的重要性 (Android 9.0+): 使用 AudioFocusRequest.Builder.setHandler(mHandler) 指定回调的 Handler
    • 默认行为: 若不指定,回调使用与主线程 Looper 绑定的 Handler,即在主线程执行
    • 风险: 如果主线程繁忙(如处理复杂 UI 或 I/O),焦点变更回调可能被延迟,导致应用响应不及时(如暂停/恢复延迟),甚至 ANR。
    • 最佳实践: 创建一个与工作线程 Looper 绑定的 Handler 并传入,确保焦点回调在非主线程及时执行。
  3. 电话焦点的特殊性 (AUDIOFOCUS_FLAG_LOCK):
    • 独占性: 只有系统电话应用(拥有 MODIFY_PHONE_STATE 权限)才能通过 requestAudioFocusForCall() 请求电话焦点 (clientId = AudioSystem.IN_VOICE_COMM_FOCUS_ID)。
    • 强制静音: 当电话激活 (mRingOrCallActive = true) 时,PlaybackActivityMonitor直接强制将所有非电话相关的活跃播放器(如 USAGE_MEDIA, USAGE_GAME)静音 (setVolume(0f)),无论这些播放器所属的应用是否正确处理了焦点丢失回调。这是系统级别的强制策略。
    • 抢占失败: 在电话激活期间,其他应用的常规 requestAudioFocus() 请求会失败。
    • 最佳实践: 应用应正确处理 AUDIOFOCUS_LOSS 回调(停止播放),但也要意识到在电话场景下,系统会直接静音,应用可能需要额外逻辑(如在 onAudioFocusChange 中检查 AudioManager.isInCommunicationCall() 或监听电话状态广播)来处理恢复或 UI 更新。
  4. PlaybackActivityMonitor 的作用: 它是系统掌握全局音频播放状态的关键。通过跟踪所有 AudioTrack / MediaPlayer 实例 (mPlayers),系统能够:
    • 强制执行电话静音/恢复。
    • 实现其他高级音频策略(如音频闪避 Ducking 的底层支持、音量控制、音频路由决策等)。
  5. 焦点栈管理: MediaFocusControl 使用栈 (mFocusStack) 管理焦点请求。遵循 LIFO (后进先出) 原则,栈顶拥有当前焦点。移除栈中元素时:
    • 移除栈顶 (signal=true):会通知新的栈顶元素获得焦点 (AUDIOFOCUS_GAIN)。
    • 移除非栈顶 (signal=false):不会触发焦点变更通知。
  6. 及时释放焦点: 当应用不再需要播放音频时(如播放完成、用户暂停、界面销毁),必须调用 abandonAudioFocus() / abandonAudioFocusRequest() 释放焦点。否则:
    • 该应用会一直占据焦点栈位置。
    • 其他应用可能无法正常获得焦点播放声音。
    • 用户期望听到的声音(如新启动的音乐 App、通知音)可能被错误地抑制。
5. 重点

Android AudioFocus 机制是一个复杂但精妙设计的系统服务组件,它通过 AudioService (特别是 MediaFocusControlPlaybackActivityMonitor) 协调所有应用的音频播放行为。理解其核心概念(焦点类型、变更回调)、关键角色交互(Binder, Handler)、核心源码流程(请求、释放、电话强制静音)以及最佳实践(clientId/Listener 复用、指定 Handler、及时释放)对于开发健壮、符合用户期望的音频应用至关重要。虽然遵循 AudioFocus 不是强制的(电话除外),但它是确保应用在各种音频场景下表现一致且良好的基石。

焦点请求队列与优先级

  • LIFO(后进先出):最新请求焦点的应用优先获得焦点。
  • 优先级规则
    • 系统应用(如电话)拥有最高优先级。
    • 前台应用优先于后台应用。
    • 用户交互中的应用优先于非交互应用。

五、相关类与函数

AudioPlaybackConfiguration(简称apc)

AudioPlaybackConfiguration 与 PlayerProxy

在AudioPlaybackConfiguration类中,有一个getPlayerProxy()方法,该方法返回一个PlayerProxy对象。这个PlayerProxy对象是在AudioPlaybackConfiguration构造时传入的,它指向了一个PlayerBase的PlayerProxy内部类实例。

PlayerBase

而PlayerBase是Android音频系统中播放器类的基类,例如AudioTrack、MediaPlayer、SoundPool等都继承自PlayerBase。

PlaybackActivityMonitor

当我们创建一个播放器(比如AudioTrack)时,在PlayerBase的构造方法中,会创建一个PlayerProxy对象,并将其设置给AudioPlaybackConfiguration。同时,这个播放器也会在PlaybackActivityMonitor中注册(通过PlayerBase的构造方法中的registerPlayer()方法),这样PlaybackActivityMonitor才能管理这些播放器。

apc.getPlayerProxy()

现在,当我们调用apc.getPlayerProxy().applyVolumeShaper(…)时,实际上是通过PlayerProxy对象调用了对应播放器(例如AudioTrack)的applyVolumeShaper方法。
具体流程如下:

IPlayer

PlayerProxy的applyVolumeShaper方法会通过一个IPlayer接口(这是一个Binder接口)调用到实际播放器端的applyVolumeShaper方法。

在播放器端(例如AudioTrack),因为它是PlayerBase的子类,而PlayerBase内部有一个BaseHandler用于处理来自PlayerProxy的请求。

在PlayerBase中,有一个内部类PlayerProxy,它实现了IPlayer接口。当PlayerProxy的applyVolumeShaper方法被调用时,它会通过PlayerBase的mWeakThis弱引用获取到PlayerBase实例,然后调用PlayerBase的applyVolumeShaper方法。

在PlayerBase的applyVolumeShaper方法中,会调用具体的播放器实现(如AudioTrack)的applyVolumeShaper方法。对于AudioTrack,它会调用native_setVolumeShaperParameters和native_applyVolumeShaper等JNI方法,最终通过AudioFlinger进行实际的音频音量调整。

调用链:

apc.getPlayerProxy().applyVolumeShaper(…)
-> 通过PlayerProxy(实现了IPlayer接口)跨进程调用(如果播放器在另一个进程)或直接调用(同一进程)到对应播放器的PlayerBase的applyVolumeShaper方法
-> 在PlayerBase的applyVolumeShaper方法中,会调用具体播放器子类(如AudioTrack)的applyVolumeShaper方法
-> 然后进入JNI层,最终通过AudioFlinger处理

注意:在同一个应用进程内,可能不会走Binder跨进程调用,因为PlayerProxy和PlayerBase都在同一个进程。但是,PlaybackActivityMonitor是在系统进程(system_server)中,而播放器在应用进程,所以这里实际上是跨进程调用。
详细说明跨进程过程:

在应用进程创建播放器(如AudioTrack)时,会通过PlayerBase的构造方法向PlaybackActivityMonitor注册。注册过程是通过IAudioService(即AudioService)的trackPlayer方法,将播放器的IPlayer代理(即PlayerProxy)传递到AudioService所在的系统进程。

在系统进程中,PlaybackActivityMonitor会保存这个IPlayer代理(即应用进程的播放器代理)。

当系统进程需要让某个播放器降音时,通过之前保存的IPlayer代理调用applyVolumeShaper方法,这个调用就会跨进程到应用进程的播放器,应用进程的播放器就会执行降音操作。

因此,apc.getPlayerProxy()返回的PlayerProxy对象实际上是一个IPlayer的代理,通过这个代理,系统进程可以控制应用进程的播放器行为。
所以,最终调用会到达应用进程中的播放器对象(如AudioTrack)的applyVolumeShaper方法,然后通过JNI调用到native层进行实际的音量调整。

六、源码解析

requestAudioFocus
1.注意

mRingOrCallActive: 系统处于电话/响铃状态
flag: AudioManager.AUDIOFOCUS_FLAG_DELAY_OK

标志位 含义 示例场景 AUDIOFOCUS_FLAG_DELAY_OK 允许延迟授予焦点(如通话中暂不中断,待通话结束再分配) 音乐应用在通话中申请焦点 AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS 当收到AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK时主动暂停(而非仅降低音量) 播客应用遇到导航提示时暂停 AUDIOFOCUS_FLAG_LOCK 锁定焦点,防止被其他请求抢占 紧急警报播放期间
 protected int requestAudioFocus(@NonNull AudioAttributes aa, int focusChangeHint, IBinder cb, IAudioFocusDispatcher fd, @NonNull String clientId, @NonNull String callingPackageName, int flags, int sdk, boolean forceDuck, int testUid, boolean permissionOverridesCheck) { new MediaMetrics.Item(mMetricsId) .setUid(Binder.getCallingUid()) .set(MediaMetrics.Property.CALLING_PACKAGE, callingPackageName) .set(MediaMetrics.Property.CLIENT_NAME, clientId) .set(MediaMetrics.Property.EVENT, \"requestAudioFocus\") .set(MediaMetrics.Property.FLAGS, flags) .set(MediaMetrics.Property.FOCUS_CHANGE_HINT, AudioManager.audioFocusToString(focusChangeHint)) //.set(MediaMetrics.Property.SDK, sdk) .record(); // when using the test API, a fake UID can be injected (testUid is ignored otherwise) // note that the test on flags is not a mask test on purpose, AUDIOFOCUS_FLAG_TEST is // supposed to be alone in bitfield final int uid = (flags == AudioManager.AUDIOFOCUS_FLAG_TEST) ? testUid : Binder.getCallingUid(); mEventLogger.enqueue((new EventLogger.StringEvent( \"requestAudioFocus() from uid/pid \" + uid  + \"/\" + Binder.getCallingPid()  + \" AA=\" + aa.usageToString() + \"/\" + aa.contentTypeToString()  + \" clientId=\" + clientId + \" callingPack=\" + callingPackageName  + \" req=\" + focusChangeHint  + \" flags=0x\" + Integer.toHexString(flags)  + \" sdk=\" + sdk)) .printLog(TAG)); // we need a valid binder callback for clients // 如果 pingBinder(),不存在了,就没有必要入栈了 if (!cb.pingBinder()) { Log.e(TAG, \" AudioFocus DOA client for requestAudioFocus(), aborting.\"); return AudioManager.AUDIOFOCUS_REQUEST_FAILED; } // mAudioFocusLock 同步锁 synchronized(mAudioFocusLock) { // check whether a focus freeze is in place and filter if (isFocusFrozenForTest()) { int focusRequesterUid; if ((flags & AudioManager.AUDIOFOCUS_FLAG_TEST) == AudioManager.AUDIOFOCUS_FLAG_TEST) {  focusRequesterUid = testUid; } else { //获取uid  focusRequesterUid = Binder.getCallingUid(); } if (isFocusFrozenForTestForUid(focusRequesterUid)) {  Log.i(TAG, \"requestAudioFocus: focus frozen for test for uid:\" + focusRequesterUid);  return AudioManager.AUDIOFOCUS_REQUEST_FAILED; } Log.i(TAG, \"requestAudioFocus: focus frozen for test but uid:\" + focusRequesterUid + \" is exempt\"); }//stack 有大小限制 if (mFocusStack.size() > MAX_STACK_SIZE) { Log.e(TAG, \"Max AudioFocus stack size reached, failing requestAudioFocus()\"); return AudioManager.AUDIOFOCUS_REQUEST_FAILED; }// mRingOrCallActive 系统处于电话/响铃状态 boolean enteringRingOrCall = !mRingOrCallActive  & (AudioSystem.IN_VOICE_COMM_FOCUS_ID.compareTo(clientId) == 0); if (enteringRingOrCall) { mRingOrCallActive = true; } final AudioFocusInfo afiForExtPolicy; if (mFocusPolicy != null) { // construct AudioFocusInfo as it will be communicated to audio focus policy afiForExtPolicy = new AudioFocusInfo(aa, uid, clientId, callingPackageName, focusChangeHint, 0 /*lossReceived*/, flags, sdk); } else { afiForExtPolicy = null; } // handle delayed focus boolean focusGrantDelayed = false; // 判断当前系统是否允许重新分配焦点,若返回 false,表示系统无法立即分配焦点​  if (!canReassignAudioFocus()) { if ((flags & AudioManager.AUDIOFOCUS_FLAG_DELAY_OK) == 0) {  return AudioManager.AUDIOFOCUS_REQUEST_FAILED; } else {  // request has AUDIOFOCUS_FLAG_DELAY_OK: focus can\'t be  // granted right now, so the requester will be inserted in the focus stack  // to receive focus later  focusGrantDelayed = true; } } // external focus policy? if (mFocusPolicy != null) { if (notifyExtFocusPolicyFocusRequest_syncAf(afiForExtPolicy, fd, cb)) {  // stop handling focus request here as it is handled by external audio  // focus policy (return code will be handled in AudioManager)  return AudioManager.AUDIOFOCUS_REQUEST_WAITING_FOR_EXT_POLICY; } else {  // an error occured, client already dead, bail early  return AudioManager.AUDIOFOCUS_REQUEST_FAILED; } } // handle the potential premature death of the new holder of the focus // (premature death == death before abandoning focus) // Register for client death notification // 用于监听客户端 Binder 死亡事件 AudioFocusDeathHandler afdh = new AudioFocusDeathHandler(cb); try { cb.linkToDeath(afdh, 0); } catch (RemoteException e) { // client has already died! Log.w(TAG, \"AudioFocus requestAudioFocus() could not link to \"+cb+\" binder death\"); return AudioManager.AUDIOFOCUS_REQUEST_FAILED; }// 焦点栈非空(!mFocusStack.empty())且栈顶焦点持有者(mFocusStack.peek())与当前请求的客户端ID一致, 表明当前应用此前已成功获取焦点且未释放 if (!mFocusStack.empty() && mFocusStack.peek().hasSameClient(clientId)) { // if focus is already owned by this client and the reason for acquiring the focus // hasn\'t changed, don\'t do anything final FocusRequester fr = mFocusStack.peek(); if (fr.getGainRequest() == focusChangeHint && fr.getGrantFlags() == flags) {  // unlink death handler so it can be gc\'ed.  // linkToDeath() creates a JNI global reference preventing collection.  cb.unlinkToDeath(afdh, 0);  notifyExtPolicyFocusGrant_syncAf(fr.toAudioFocusInfo(), AudioManager.AUDIOFOCUS_REQUEST_GRANTED);  //音乐播放器的重复请求 //应用已通过 requestAudioFocus() 获取永久焦点(如 AUDIOFOCUS_GAIN)并播放音乐。 //播放过程中因内部状态刷新(如切歌、界面重建)重复发起相同参数的焦点请求。 //系统检测到参数未变化,直接复用现有焦点,避免冗余操作,打印日志并返回 AUDIOFOCUS_REQUEST_GRANTED。  return AudioManager.AUDIOFOCUS_REQUEST_GRANTED; } // the reason for the audio focus request has changed: remove the current top of // stack and respond as if we had a new focus owner if (!focusGrantDelayed) {  mFocusStack.pop();  // the entry that was \"popped\" is the same that was \"peeked\" above  fr.release(); } } // focus requester might already be somewhere below in the stack, remove it removeFocusStackEntry(clientId, false /* signal */, false /*notifyFocusFollowers*/); final FocusRequester nfr = new FocusRequester(aa, focusChangeHint, flags, fd, cb,  clientId, afdh, callingPackageName, uid, this, sdk, mEventLogger); if (mMultiAudioFocusEnabled  && (focusChangeHint == AudioManager.AUDIOFOCUS_GAIN)) { if (enteringRingOrCall) {  if (!mMultiAudioFocusList.isEmpty()) { for (FocusRequester multifr : mMultiAudioFocusList) { multifr.handleFocusLossFromGain(focusChangeHint, nfr, forceDuck); }  } } else {  boolean needAdd = true;  if (!mMultiAudioFocusList.isEmpty()) { for (FocusRequester multifr : mMultiAudioFocusList) { if (multifr.getClientUid() == Binder.getCallingUid()) { needAdd = false; break; } }  }  if (needAdd) { mMultiAudioFocusList.add(nfr);  }  nfr.handleFocusGainFromRequest(AudioManager.AUDIOFOCUS_REQUEST_GRANTED);  notifyExtPolicyFocusGrant_syncAf(nfr.toAudioFocusInfo(), AudioManager.AUDIOFOCUS_REQUEST_GRANTED);  return AudioManager.AUDIOFOCUS_REQUEST_GRANTED; } } if (focusGrantDelayed) { // focusGrantDelayed being true implies we can\'t reassign focus right now // which implies the focus stack is not empty. final int requestResult = pushBelowLockedFocusOwnersAndPropagate(nfr); if (requestResult != AudioManager.AUDIOFOCUS_REQUEST_FAILED) {  notifyExtPolicyFocusGrant_syncAf(nfr.toAudioFocusInfo(), requestResult); } return requestResult; } else { // propagate the focus change through the stack propagateFocusLossFromGain_syncAf(focusChangeHint, nfr, forceDuck); // push focus requester at the top of the audio focus stack mFocusStack.push(nfr); nfr.handleFocusGainFromRequest(AudioManager.AUDIOFOCUS_REQUEST_GRANTED); } notifyExtPolicyFocusGrant_syncAf(nfr.toAudioFocusInfo(),  AudioManager.AUDIOFOCUS_REQUEST_GRANTED); if (ENFORCE_MUTING_FOR_RING_OR_CALL & enteringRingOrCall) { runAudioCheckerForRingOrCallAsync(true/*enteringRingOrCall*/); } }//synchronized(mAudioFocusLock) return AudioManager.AUDIOFOCUS_REQUEST_GRANTED; }
focusOwner.hasSameUid(uid)

用于检查音频焦点所有者是否属于特定的用户 ID(UID)

1 返回值

true:焦点所有者的 UID 与传入的 UID 相同
false:UID 不同