当其他应用获得音频焦点时,你的应用应该如何响应?

解读

国内面试中,这道题考察的是“音频焦点”这一 Android 音频系统的核心机制。面试官想确认两点:

  1. 你是否真正理解“焦点”是系统级仲裁,而非简单静音;
  2. 你是否能把官方文档里的“建议”落地成符合国内厂商 ROM、国内业务场景(如直播、短视频、语音通话、车载蓝牙)的健壮代码。
    答得太浅(“把 MediaPlayer pause 就行”)会被追问“耳机、蓝牙、车载、多窗口、折叠屏分屏怎么办”;答得太偏(“自己写 JNI 去调 alsa”)又会被认为过度设计。因此,要围绕 AudioManager.AudioFocusRequest + OnAudioFocusChangeListener 给出“系统回调 → 业务决策 → 状态恢复”三段式方案,并主动提及国内常见的“来电+高德+微信语音”并发场景。

知识点

  1. 音频焦点三要素
    • 请求方:AudioManager.requestAudioFocus(AudioFocusRequest)
    • 监听方:OnAudioFocusChangeListener(int focusChange)
    • 释放方:abandonAudioFocus()
  2. 焦点类型(SDK 34 仍兼容)
    • AUDIOFOCUS_GAIN:长期占有,例如音乐播放器
    • AUDIOFOCUS_GAIN_TRANSIENT:瞬时占有,例如微信语音消息
    • AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK:可压低背景,例如导航播报
  3. 焦点丢失码
    • AUDIOFOCUS_LOSS:永久丢失,必须暂停并释放资源
    • AUDIOFOCUS_LOSS_TRANSIENT:临时丢失,暂停但保留资源,等待后续 GAIN
    • AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:只需降低音量,不允许暂停(否则用户体验割裂)
  4. 国内特殊场景
    • 华为/小米/OPPO 定制 ROM 对“电话”和“录音”有系统级强制抢占,回调线程可能延迟 100-200 ms,需在主线程再做一次状态校验
    • 车载通道(Bluetooth A2DP / USB Audio)焦点切换时,系统会发送 android.media.AUDIO_BECOMING_NOISY 广播,必须一起监听
    • 折叠屏分屏时,两个可见应用可能同时播放,焦点策略与主屏一致,但用户感知更强,需给出 UI 提示
  5. 生命周期绑定
    • 在 Activity.onPause / Service.onDestroy 时 abandonAudioFocus,防止内存泄漏与“幽灵”焦点
    • 使用 LifecycleObserver 自动注册/反注册,适配 Jetpack 组件化架构
  6. 合规与隐私
    • 国内应用市场(应用宝、华为 AppGallery)在 2024 年新审核指南中,把“后台偷放音频”列为高危,必须证明已正确处理 LOSS 事件,否则下架

答案

“当系统通知我们失去音频焦点时,我会按以下三步处理:

  1. 区分丢失类型:在 OnAudioFocusChangeListener 中判断 focusChange 值。
    • 若是 AUDIOFOCUS_LOSS,立即 pause 播放并释放 MediaPlayer/ExoPlayer,同时记录当前进度, abandonAudioFocus 防止占用;
    • 若是 AUDIOFOCUS_LOSS_TRANSIENT,仅 pause 不释放,保留进度与缓冲,等待后续 AUDIOFOCUS_GAIN 再自动 resume;
    • 若是 AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK,把音量降到 20%-30%,不暂停,保证导航/提示音叠加时用户仍可听清背景音乐。
  2. 业务层防抖:国内 ROM 回调线程可能延迟,我会把状态写入一个 AudioStateLiveData,通过主线程 Handler 再校验一次,避免重复 pause/resume 导致爆音。
  3. 恢复策略:当收到 AUDIOFOCUS_GAIN 时,先检查当前播放场景(耳机、蓝牙、车载),再决定是否 resume、恢复到原音量,并做 200 ms 淡入动画,防止突兀。
    此外,我会同时注册 BECOMING_NOISY 广播,当耳机拔出时主动 pause;在 Service 中持有音频焦点时,绑定到 Lifecycle,一旦用户划走或来电,立即 abandon,确保不违反国内商店后台音频政策。”

拓展思考

  1. 多播放器并存:如果业务里同时有 BGM 和音效两条轨道,可以为它们分别申请 AudioFocusRequest,setAudioAttributes 使用 CONTENT_TYPE_MUSIC 与 CONTENT_TYPE_SONIFICATION,系统会按同一 UID 合并管理,但回调仍分开,方便独立 duck。
  2. 与通话模块共存:国内项目普遍使用 TelecomManager 或第三方 VoIP SDK,通话已占用 STREAM_VOICE_CALL,此时音乐流应主动检测 CallState,而不是仅依赖焦点回调,否则会出现“双通道”并发。
  3. 未来趋势:Android 15 将引入“AppVolume”与“FocusGroup”概念,允许用户给不同应用设音量,面试时可提及“我们会关注 AOSP mainline 的 AudioManager 变更,提前在 beta 设备验证,确保升级后无回归”。