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

React Native原生模块听筒播放音频无声音,求解决方案

React Native Android听筒播放音频问题排查与修复

问题点分析

你的代码存在几个关键问题导致听筒无声音:

  • 模块名非法:getName()返回的"Earpiece Module"包含空格,React Native原生模块名不允许有空格,会导致JS端无法正确调用该模块。
  • 无效的播放状态判断:playWithMedia里的isPlaying是局部变量,永远为false,每次调用都会创建新的MediaPlayer实例,引发资源冲突或泄漏。
  • 异步prepare后直接start:prepareAsync()是异步操作,立刻调用start()会触发IllegalStateException,MediaPlayer还未准备完成就被启动。
  • 未指定音频流类型:没有将MediaPlayer的音频流设置为STREAM_VOICE_CALL,这是音频走听筒的核心配置。
  • 未请求音频焦点:修改音频模式和听筒状态前未获取音频焦点,系统可能会忽略你的设置。
  • 缺少必要权限:未声明MODIFY_AUDIO_SETTINGS权限,无法修改系统音频配置。
  • 音频模式设置时机错误:音频模式和听筒状态应该在MediaPlayer准备完成后设置,且播放结束后需要恢复原模式,避免影响系统音频。

修复后的代码

package com.modules;

import android.content.Context;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.net.Uri;
import androidx.annotation.NonNull;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;

public class EarpieceModule extends ReactContextBaseJavaModule {
    @NonNull
    @Override
    public String getName() {
        return "EarpieceModule"; // 去掉空格,符合RN模块命名规范
    }

    private final ReactContext context;
    private MediaPlayer mediaPlayer;
    private AudioManager audioManager;
    private int originalAudioMode; // 保存原音频模式,用于播放结束后恢复

    public EarpieceModule(ReactContext context) {
        super(context);
        this.context = context;
        audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
    }

    @ReactMethod
    public void playWithMedia(String path) {
        // 检查当前是否正在播放,避免重复创建实例
        if (mediaPlayer != null && mediaPlayer.isPlaying()) {
            return;
        }

        try {
            mediaPlayer = new MediaPlayer();
            // 设置音频流类型为语音通话流,强制走听筒
            mediaPlayer.setAudioStreamType(AudioManager.STREAM_VOICE_CALL);
            mediaPlayer.setDataSource(getReactApplicationContext(), Uri.parse(path));

            // 请求音频焦点
            AudioManager.OnAudioFocusChangeListener focusChangeListener = focusChange -> {
                if (focusChange == AudioManager.AUDIOFOCUS_LOSS) {
                    stopMedia();
                }
            };
            int focusResult = audioManager.requestAudioFocus(
                    focusChangeListener,
                    AudioManager.STREAM_VOICE_CALL,
                    AudioManager.AUDIOFOCUS_GAIN_TRANSIENT
            );

            if (focusResult != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
                // 音频焦点请求失败,停止操作
                if (mediaPlayer != null) {
                    mediaPlayer.release();
                    mediaPlayer = null;
                }
                return;
            }

            // 保存原音频模式
            originalAudioMode = audioManager.getMode();

            mediaPlayer.setOnPreparedListener(mp -> {
                // 准备完成后设置音频模式和听筒
                audioManager.setMode(AudioManager.MODE_IN_CALL);
                audioManager.setSpeakerphoneOn(false);
                mp.start();
            });

            // 播放结束后恢复音频模式
            mediaPlayer.setOnCompletionListener(mp -> {
                restoreAudioSettings();
                stopMedia();
            });

            mediaPlayer.prepareAsync();
        } catch (Exception e) {
            e.printStackTrace();
            restoreAudioSettings();
            if (mediaPlayer != null) {
                mediaPlayer.release();
                mediaPlayer = null;
            }
        }
    }

    @ReactMethod
    public void stopMedia() {
        if (mediaPlayer != null) {
            if (mediaPlayer.isPlaying()) {
                mediaPlayer.stop();
            }
            mediaPlayer.reset();
            mediaPlayer.release();
            mediaPlayer = null;
            restoreAudioSettings();
            // 放弃音频焦点
            audioManager.abandonAudioFocus(null);
        }
    }

    // 恢复原音频设置
    private void restoreAudioSettings() {
        try {
            audioManager.setMode(originalAudioMode);
            audioManager.setSpeakerphoneOn(audioManager.isSpeakerphoneOn());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

额外配置

在AndroidManifest.xml中添加必要权限:

<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />

JS端调用注意

确保JS端调用的模块名和getName()返回的一致:

import { NativeModules } from 'react-native';
const { EarpieceModule } = NativeModules;

// 播放音频
EarpieceModule.playWithMedia('file:///path/to/your/audio.mp3');

// 停止播放
EarpieceModule.stopMedia();

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 17:18:11