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

.NET 7 MAUI安卓端AudioRecord重启录制无音频流问题

.NET 7 MAUI Android AudioRecord停止后无法重启录制问题

问题场景

在.NET 7 MAUI应用中,通过依赖注入将封装Android AudioRecord的AndroidAudioRecordService注入ViewModel,需实现:

  • 点击启动按钮开始麦克风音频捕获
  • 点击停止按钮暂停录制
  • 再次点击启动按钮恢复录制

问题现象:首次启动录制可正常获取音频流,但停止后再次启动,无法捕获任何音频。

已尝试方案

  • 将服务注册为Transient、Scoped、Singleton三种生命周期
  • 在构造函数和每次点击启动按钮时初始化AudioRecord
  • 定位到问题与audioRecord.Stop()方法相关:注释该方法后功能正常;但直接在ViewModel中实现AudioRecord逻辑时,调用.Stop()可正常工作

问题代码

#if ANDROID
using Android.Media;

namespace Neuron.Nexus.Services;
public interface IAndroidAudioRecordService : IDisposable
{
    void StartRecording();
    void StopRecording();
    Task<(byte[] buffer, int bytesRead)> GetAudioStream();
    bool IsRecording { get; }
}

public class AndroidAudioRecordService : IAndroidAudioRecordService
{
    private AudioRecord audioRecord;
    private readonly int sampleRate = 44100;
    private readonly int bufferSize = 1024;
    private readonly ChannelIn channelConfig = ChannelIn.Mono;
    private readonly Encoding audioFormat = Encoding.Pcm16bit;
    private readonly byte[] audioBuffer;

    public bool IsRecording { get; private set; }

    public AndroidAudioRecordService()
    {
        audioBuffer = new byte[bufferSize];
    }

    public void StartRecording()
    {
        if (audioRecord != null)
        {
            audioRecord.Stop();
            audioRecord.Release();
            audioRecord.Dispose();
            audioRecord = null;

        }

        audioRecord = new AudioRecord(AudioSource.Mic, sampleRate, channelConfig, audioFormat, bufferSize);

        if (audioRecord != null && audioRecord.State == State.Initialized)
        {
            audioRecord.StartRecording();
            IsRecording = true;
        }
    }

    public void StopRecording()
    {
        if (audioRecord != null && audioRecord.RecordingState == RecordState.Recording)
        {
            audioRecord.Stop();
            IsRecording = false;
        }
    }

    public async Task<(byte[] buffer, int bytesRead)> GetAudioStream()
    {
        if (audioRecord == null)
        {
            throw new InvalidOperationException("AudioRecord is not initialized or not in recording state.");
        }

        int bytesRead = await audioRecord.ReadAsync(audioBuffer, 0, audioBuffer.Length);
        return (audioBuffer, bytesRead);
    }


    public void Dispose()
    {
        if (audioRecord != null)
        {
            if (IsRecording)
            {
                audioRecord.Stop();
            }

            audioRecord.Release();
            audioRecord.Dispose();
            audioRecord = null;
            IsRecording = false;
        }
    }
}
#endif

解决方案

问题分析

当前代码每次启动录制时都会销毁并重建AudioRecord实例,结合Stop()方法调用,可能导致Android系统麦克风资源释放不及时,或残留未完成的异步读取任务干扰新实例工作。此外,硬编码的缓冲区大小可能不符合系统要求,引发录制异常。

修改后的代码

#if ANDROID
using Android.Media;
using System.Threading;

namespace Neuron.Nexus.Services;
public interface IAndroidAudioRecordService : IDisposable
{
    void StartRecording();
    void StopRecording();
    Task<(byte[] buffer, int bytesRead)> GetAudioStream();
    bool IsRecording { get; }
}

public class AndroidAudioRecordService : IAndroidAudioRecordService
{
    private AudioRecord audioRecord;
    private readonly int sampleRate = 44100;
    private readonly ChannelIn channelConfig = ChannelIn.Mono;
    private readonly Encoding audioFormat = Encoding.Pcm16bit;
    private readonly byte[] audioBuffer;
    private CancellationTokenSource recordingCts;

    public bool IsRecording { get; private set; }

    public AndroidAudioRecordService()
    {
        // 使用系统推荐的最小缓冲区大小,避免硬编码兼容性问题
        int minBufferSize = AudioRecord.GetMinBufferSize(sampleRate, channelConfig, audioFormat);
        // 确保缓冲区大小为合理值
        int bufferSize = minBufferSize > 1024 ? minBufferSize : 1024;
        audioBuffer = new byte[bufferSize];
    }

    public void StartRecording()
    {
        // 取消上一次录制的异步任务,避免资源泄漏
        recordingCts?.Cancel();
        recordingCts?.Dispose();
        recordingCts = new CancellationTokenSource();

        // 复用已初始化的AudioRecord实例,减少资源频繁申请释放
        if (audioRecord == null)
        {
            int minBufferSize = AudioRecord.GetMinBufferSize(sampleRate, channelConfig, audioFormat);
            audioRecord = new AudioRecord(AudioSource.Mic, sampleRate, channelConfig, audioFormat, minBufferSize);
        }

        if (audioRecord != null && audioRecord.State == State.Initialized)
        {
            if (audioRecord.RecordingState != RecordState.Recording)
            {
                audioRecord.StartRecording();
                IsRecording = true;
            }
        }
    }

    public void StopRecording()
    {
        if (audioRecord != null && audioRecord.RecordingState == RecordState.Recording)
        {
            audioRecord.Stop();
            IsRecording = false;
        }
        // 取消异步读取任务,防止后续读取旧实例数据
        recordingCts?.Cancel();
    }

    public async Task<(byte[] buffer, int bytesRead)> GetAudioStream()
    {
        if (audioRecord == null || !IsRecording)
        {
            throw new InvalidOperationException("AudioRecord is not initialized or not in recording state.");
        }

        // 使用取消令牌,确保停止录制时能终止读取任务
        int bytesRead = await audioRecord.ReadAsync(audioBuffer, 0, audioBuffer.Length, recordingCts.Token);
        return (audioBuffer, bytesRead);
    }

    public void Dispose()
    {
        recordingCts?.Cancel();
        recordingCts?.Dispose();
        
        if (audioRecord != null)
        {
            if (IsRecording)
            {
                audioRecord.Stop();
            }

            audioRecord.Release();
            audioRecord.Dispose();
            audioRecord = null;
            IsRecording = false;
        }
    }
}
#endif

关键修改点

  1. 复用AudioRecord实例:不再每次启动都销毁重建,而是复用已初始化的实例,减少系统资源的频繁申请释放
  2. 使用系统推荐缓冲区大小:通过AudioRecord.GetMinBufferSize()获取符合系统要求的缓冲区大小,避免硬编码导致的兼容性问题
  3. 添加异步任务取消机制:使用CancellationTokenSource管理录制期间的异步读取任务,停止录制时取消未完成的任务,避免资源泄漏
  4. 优化状态判断:启动录制前检查当前录制状态,避免重复调用StartRecording()引发异常

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 04:45:03