You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在不监听事件的情况下获取React Native可用音频设备列表?

直接获取可用音频设备列表的实现方案

方案1:扩展原生模块,新增同步获取方法

react-native-incall-manager本身的JS API没有直接提供主动获取设备列表的接口,但可以通过扩展原生模块来实现:

Android端实现

修改InCallManagerModule.java,添加一个获取设备列表的方法:

@ReactMethod
public void getAvailableAudioDevices(Promise promise) {
    AudioManager audioManager = (AudioManager) getReactApplicationContext().getSystemService(Context.AUDIO_SERVICE);
    List<String> devices = new ArrayList<>();
    
    // 检查蓝牙设备
    if (audioManager.isBluetoothScoAvailableOffCall()) {
        devices.add("bluetooth");
    }
    // 检查扬声器
    if (audioManager.isSpeakerphoneOn()) {
        devices.add("speaker");
    }
    // 检查听筒
    devices.add("earpiece");
    // 检查有线耳机
    if (audioManager.isWiredHeadsetOn()) {
        devices.add("headphones");
    }
    
    promise.resolve(devices);
}

JS层调用方式:

import InCallManager from 'react-native-incall-manager';

async function fetchAudioDevices() {
  try {
    const devices = await InCallManager.getAvailableAudioDevices();
    return devices;
  } catch (err) {
    console.error('获取音频设备失败:', err);
    return [];
  }
}

iOS端实现

修改InCallManager.m,添加对应方法:

#import <AVFoundation/AVFoundation.h>

RCT_EXPORT_METHOD(getAvailableAudioDevices:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
    AVAudioSession *session = [AVAudioSession sharedInstance];
    NSMutableArray *devices = [NSMutableArray array];
    
    for (AVAudioSessionPortDescription *port in session.availableInputs) {
        if ([port.portType isEqualToString:AVAudioSessionPortBluetoothA2DP] || [port.portType isEqualToString:AVAudioSessionPortBluetoothHFP]) {
            [devices addObject:@"bluetooth"];
        } else if ([port.portType isEqualToString:AVAudioSessionPortBuiltInSpeaker]) {
            [devices addObject:@"speaker"];
        } else if ([port.portType isEqualToString:AVAudioSessionPortHeadphones]) {
            [devices addObject:@"headphones"];
        } else if ([port.portType isEqualToString:AVAudioSessionPortBuiltInMic]) {
            [devices addObject:@"earpiece"];
        }
    }
    
    resolve(devices);
}

JS层调用和Android端一致。

方案2:缓存事件返回的设备列表(无需修改原生)

如果不想动原生代码,可以在应用初始化时就监听onAudioDeviceChanged事件,把返回的设备列表缓存到全局变量,之后直接读取缓存即可:

import InCallManager from 'react-native-incall-manager';

// 全局缓存设备列表
let cachedAudioDevices = [];

// 初始化时监听事件并更新缓存
InCallManager.addEventListener('onAudioDeviceChanged', (data) => {
  cachedAudioDevices = data.availableAudioDeviceList;
});

// 主动获取设备列表的方法
function getAvailableAudioDevices() {
  // 首次调用如果缓存为空,临时切换音频路由触发事件更新缓存
  if (cachedAudioDevices.length === 0) {
    const currentRoute = InCallManager.getAudioRoute();
    InCallManager.setAudioRoute('speaker');
    setTimeout(() => {
      InCallManager.setAudioRoute(currentRoute);
    }, 100);
  }
  return cachedAudioDevices;
}

注意:首次调用可能需要等待事件触发,建议在应用启动后提前初始化监听。

结合React-Native-WebRTC的补充方案

WebRTC的mediaDevices也能获取设备列表,可作为辅助手段:

import { mediaDevices } from 'react-native-webrtc';

async function getWebRTCAudioDevices() {
  const allDevices = await mediaDevices.enumerateDevices();
  const audioDevices = allDevices.filter(device => device.kind.includes('audio'));
  
  // 映射成和incall-manager一致的命名格式
  return audioDevices.map(device => {
    if (device.label.includes('Bluetooth')) return 'bluetooth';
    if (device.label.includes('Speaker')) return 'speaker';
    if (device.label.includes('Headphones')) return 'headphones';
    return 'earpiece';
  });
}

这种方式的设备标签可能因机型差异有所不同,需要做兼容处理。

内容的提问来源于stack exchange,提问作者vuminh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 00:10:20