简介: CSDN博客专家、《Android系统多媒体进阶实战》作者
博主新书推荐:《Android系统多媒体进阶实战》🚀
Android Audio工程师专栏地址:Audio工程师进阶系列【原创干货持续更新中...... 】🚀
Android多媒体专栏地址:多媒体系统工程师系列【原创干货持续更新中...... 】🚀
专题一 二:AAOS车载系统+AOSP14系统攻城狮入门视频实战课 🚀
专题三:Android14 Binder之HIDL与AIDL通信实战课 🚀
专题四:Android15快速自定义与集成音效实战课 🚀
专题五:Android15音频策略实战课 🚀
专题六:Android15音频性能实战课(无声/杂音/断音/爆音实战案例) 🚀
人生格言: 人生从来没有捷径,只有行动才是治疗恐惧和懒惰的唯一良药.
更多原创,欢迎关注:Android系统攻城狮

🍉🍉🍉文章目录🍉🍉🍉
-
-
- [🌻1. 前言](#🌻1. 前言)
- [🌻2. 用法与应用场景](#🌻2. 用法与应用场景)
- [🌻3. 调用流程剖析](#🌻3. 调用流程剖析)
-
- [3.1 核心步骤](#3.1 核心步骤)
- [3.2 涉及核心时序图](#3.2 涉及核心时序图)
- [🌻4. 实战应用案例](#🌻4. 实战应用案例)
- [🌻5. 用法总结](#🌻5. 用法总结)
-
🌻1. 前言
本篇目的:Android16音频深度解析之 MediaRecorder.getPreferredDevice 调用流程与实战。
在 Android 录音开发中,精准控制音频输入源(Input Source)是保证通话质量或录音效果的前提。与播放端的 MediaPlayer 类似,MediaRecorder 也实现了 AudioRouting 接口。通过 getPreferredDevice,开发者可以获取当前为录音任务手动指定的 首选输入设备。这在多麦克风阵列或外接专业声卡的场景下,是确保数据流从正确物理端口进入系统的核心手段。
🌻2. 用法与应用场景
MediaRecorder.getPreferredDevice 方法用于返回通过 setPreferredDevice(AudioDeviceInfo) 显式绑定的输入设备信息。
- 用法说明 :该方法返回一个
AudioDeviceInfo对象。如果开发者未曾手动指定输入设备,或者调用了setPreferredDevice(null),则该方法返回null,意味着录音路由将由AudioPolicyManager根据系统默认策略(如:外接麦克风优先)自动决定。 - 运行结果:明确当前录音实例"期望"绑定的物理输入端口。
- 应用场景 :
- 输入源锁定校验:在启动采访录音前,确认系统是否已正确锁定到外接的指向性麦克风。
- 双路录音区分:在同时开启两个录音实例时(如:通话录音与环境音录制),通过此接口确保两个实例各自绑定了不同的物理设备。
- UI 动态提示:实时告知用户当前生效的录音设备(如:手机底部麦克风、顶部麦克风或蓝牙耳机麦克风)。
🌻3. 调用流程剖析
3.1 核心步骤
- Java 层拦截 :应用层调用
MediaRecorder.getPreferredDevice()。Java 框架层维护着一个mPreferredDevice的变量,作为指令的初步缓存。 - Native 实例同步 :调用通过 JNI 进入
android_media_MediaRecorder.cpp。MediaRecorder在底层实际上是管理着一个AudioRecord实例或类似的音频流节点。 - AudioPolicy 句柄查询 :指令下发至
MediaServer进程。MediaRecorder会询问AudioPolicyService:当前该 Session ID 绑定的手动路由句柄(Handle)是什么。 - 设备信息解封 :
AudioPolicyManager返回对应的物理端口 ID。Native 层将其映射为设备描述符,最终转换回 Java 层的AudioDeviceInfo。 - 状态一致性 :需要注意的是,
getPreferredDevice获取的是"设置值",而真正物理生效的设备应通过监听onRoutingChanged并调用getRoutedDevice()获取。
3.2 涉及核心时序图
AudioPolicyManager AudioPolicyService MediaRecorder Native MediaRecorder Java 应用代码层 AudioPolicyManager AudioPolicyService MediaRecorder Native MediaRecorder Java 应用代码层 alt [缓存有效] [需同步底层] 调用 getPreferredDevice() 检查内部 mPreferredDevice 变量 返回 AudioDeviceInfo 调用 native_get_preferred_device 跨进程请求当前 Session 路由绑定 查询指定 Client 的 Preferred Device 返回设备端口 ID 返回设备 Handle 转换并返回设备信息 返回 AudioDeviceInfo
🌻4. 实战应用案例
本案例展示了如何为 MediaRecorder 指定特定的麦克风,并验证其绑定状态。
java
public class RecordingRoutingManager {
private MediaRecorder recorder;
private AudioManager audioManager;
public void initRecorder(Context context) {
audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
recorder = new MediaRecorder(context);
}
/**
* 锁定特定输入设备并校验
*/
public void setupInputDevice(int targetDeviceType) {
if (recorder == null) return;
// 1. 获取所有输入设备(麦克风)
AudioDeviceInfo[] inputDevices = audioManager.getDevices(AudioManager.GET_DEVICES_INPUTS);
AudioDeviceInfo selectedDevice = null;
for (AudioDeviceInfo device : inputDevices) {
if (device.getType() == targetDeviceType) {
selectedDevice = device;
break;
}
}
if (selectedDevice != null) {
// 2. 设置首选录音设备
boolean isSet = recorder.setPreferredDevice(selectedDevice);
if (isSet) {
// 3. 立即通过 getPreferredDevice 确认
AudioDeviceInfo currentPreferred = recorder.getPreferredDevice();
if (currentPreferred != null && currentPreferred.getId() == selectedDevice.getId()) {
System.out.println("录音设备锁定成功: " + currentPreferred.getProductName());
}
}
}
}
public void startRecording(String path) {
try {
recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
recorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
recorder.setOutputFile(path);
// 注册路由变更监听,观察物理切换
recorder.addOnRoutingChangedListener(routing -> {
AudioDeviceInfo realDevice = routing.getRoutedDevice();
System.out.println("录音实际流向设备: " + (realDevice != null ? realDevice.getProductName() : "未知"));
}, new Handler(Looper.getMainLooper()));
recorder.prepare();
recorder.start();
} catch (IOException e) {
e.printStackTrace();
}
}
public void stop() {
if (recorder != null) {
recorder.stop();
recorder.release();
recorder = null;
}
}
}
🌻5. 用法总结
| 调用层级 | 核心职责 | 关键特性/影响 |
|---|---|---|
| 应用框架层 | 维护首选设备变量与接口映射 | 决定了开发者对输入源的"控制意图" |
| 系统服务层 | 跨进程传递路由控制指令 | 建立 MediaRecorder 与 AudioPolicy 的关联 |
| 音频策略层 | 执行录音路径决策与冲突处理 | 优先满足 PreferredDevice 的手动请求 |
| 音频混音层 | 管理录音数据流的 Patch 链路 | 确保采样数据从指定物理 Port 读入 |
| 硬件抽象层 | 激活物理麦克风通路 | 影响增益(Gain)与偏置设置 |
最优实战方案落地步骤:
- 明确输入需求 :根据业务(如:降噪录音、立体声录制)从
AudioManager.getDevices中筛选合适的AudioDeviceInfo。 - 时机控制 :
setPreferredDevice可以在MediaRecorder实例化后的任何时候调用,但建议在prepare()之前完成绑定。 - 结果校验 :通过
getPreferredDevice确认逻辑层绑定成功。 - 闭环监控 :必须结合
getRoutedDevice和OnRoutingChangedListener,以防手动指定的设备(如蓝牙麦克风)在录音中途意外断开。