PAYATHON 2026

Android 16 如何将 STREAM_MUSIC 全局切换到内置扬声器?

数据阿明

普通应用能否直接完成全局切换?

对于没有系统特权或专用角色授权的普通第三方应用,Android 16 没有提供可通用调用的公开 API,让应用在 MODE_NORMAL 下强制将整部手机的 STREAM_MUSIC 切换到内置扬声器。应用可以为自己的播放器请求使用指定设备;控制系统媒体路由则涉及受限制的权限和系统接口。系统音频路由说明、MediaRouter2 文档

Android 中的常量名是 AudioDeviceInfo.TYPE_BUILTIN_SPEAKER,问题中的 TYPE_BUILDIN_SPEAKER 拼写有误,不能直接用于代码。AudioDeviceInfo 文档

为什么原来的 API 无法满足需求?

AudioManager.setSpeakerphoneOn() 在 API level 34 被弃用,替代方法 setCommunicationDevice() 明确用于语音、视频通话等通信场景,不能作为普通媒体播放的全局设备开关。为了让音乐外放而改成 MODE_IN_COMMUNICATION,也不符合保持 MODE_NORMAL 的需求。AudioManager 文档

如果问题中的 setAudioRoute() 指的是旧版 AudioManager 路由方法,需要核对实际调用:该类的旧方法名是 setRouting(),在 API level 15 就已弃用;如果该方法来自其他库,则应按对应库的接口判断。旧路由 API 文档

AudioAttributes 描述音频用途、内容类型等属性,本身不负责选择物理扬声器。要为本应用的播放器指定输出设备,应使用播放器支持的设备选择接口。

只切换本应用的声音时怎么做?

如果应用持有自己的 AudioTrack,可以枚举输出设备,找到内置扬声器后调用 setPreferredDevice()。下面的 Kotlin 示例接收一个已经初始化的 AudioTrack:

import android.content.Context
import android.media.AudioDeviceInfo
import android.media.AudioManager
import android.media.AudioTrack

fun requestBuiltInSpeaker(
    context: Context,
    track: AudioTrack
): Boolean {
    val audioManager =
        context.getSystemService(AudioManager::class.java)

    val speaker = audioManager
        .getDevices(AudioManager.GET_DEVICES_OUTPUTS)
        .firstOrNull {
            it.type == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER
        } ?: return false

    return track.setPreferredDevice(speaker)
}

这个调用只设置传入的 AudioTrack 的首选输出,不会修改其他应用的播放器。传入 null 可以恢复默认路由。AudioTrack 设备选择接口

返回 true 后仍需检查实际路由。getPreferredDevice() 返回首选设备,不保证它就是正在使用的设备。Android 16 的 API level 36 提供了 getRoutedDevices();应在播放器正在播放且收到路由变化通知后检查:

// Android 16 / API level 36;track 必须正在播放。
val usesSpeaker = track.getRoutedDevices().any {
    it.type == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER
}

可以通过 AudioRouting.OnRoutingChangedListener 监听路由变化。设备插拔或系统策略改变后,还应重新确认实际输出。AudioRouting 文档

全局媒体路由需要什么权限?

系统层有按音频策略选择首选设备的接口,例如 setPreferredDeviceForStrategy()、setPreferredDevicesForStrategy()。它们属于系统 API,不能作为普通应用 SDK 中直接替换旧方法的接口。AOSP 系统接口说明

MediaRouter2 也提供代理控制其他应用或整个系统路由的能力。例如,带目标包名的代理实例接口允许用 "android" 表示整个系统,但需要 MEDIA_CONTENT_CONTROL 或 MEDIA_ROUTING_CONTROL。MediaRouter2 权限要求

这两项权限有明确限制:MEDIA_CONTENT_CONTROL 不面向普通第三方应用,MEDIA_ROUTING_CONTROL 仅供 COMPANION_DEVICE_WATCH 角色使用。因此,普通应用仅在 Manifest 中声明权限,不能据此获得全局路由控制能力。MODIFY_AUDIO_SETTINGS 也不等于拥有上述授权。权限定义

如果开发的是厂商系统应用,应结合实际系统授权和音频策略处理;如果开发的是面向普通用户安装的应用,应把全局切换交给系统提供的媒体输出界面,并接受设备可能不提供目标选项的限制。

Spotify 和 Samsung 的现象如何判断?

Spotify 中出现 Wired Connection 和 This Phone,切换后其他应用也受到影响,这些观察不足以确定 Spotify 内部使用了哪个接口。操作最终由系统媒体路由组件执行是一种可能的解释,但这不能直接证明普通应用可以自行调用相同的全局切换能力。

系统输出选择器的外观和具体行为可能随设备、系统版本及形态变化。Samsung 上缺少 This Phone,需要结合具体机型、One UI 版本和连接设备排查;现有信息不足以认定是 One UI 缺陷。系统输出选择器说明

对于“只插入 USB 麦克风也停止外放”的情况,可以先分别枚举 GET_DEVICES_INPUTS 和 GET_DEVICES_OUTPUTS,确认该设备是否同时暴露了音频输出端点,再检查正在播放的音轨的实际路由,以区分 USB 设备识别、系统默认路由选择和播放器自身设置的问题。

备注:内容仅供参考。