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

MediaRecorder启动抛出IllegalStateException,无法获取音频振幅求助

解决MediaRecorder.start()抛出IllegalStateException(错误码-38)的问题

我来帮你排查这个音频振幅测量的问题,错误码-38和IllegalStateException通常和MediaRecorder的状态机违规或者资源/权限问题有关,结合你的代码和需求,咱们一步步拆解解决方案:

一、先解决核心冲突:避免重复启动与状态混乱

你的start()方法仅通过mRecorder == null判断是否初始化,但如果之前的录制未彻底停止(比如stop()执行异常导致mRecorder未被置空),再次调用start()就会触发状态机异常。建议添加一个录制状态标记,严格管控流程:

private boolean isRecording = false;

public void start() {
    if (mRecorder == null && !isRecording) {
        try {
            mRecorder = new MediaRecorder();
            mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
            // 换用更通用的格式组合,AMR_WB并非所有设备都支持
            mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
            mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
            
            // 替换/dev/null为应用私有临时文件,部分设备对/dev/null支持不佳
            File tempFile = File.createTempFile("audio_temp", ".3gp", getCacheDir());
            mRecorder.setOutputFile(tempFile.getAbsolutePath());
            
            try {
                mRecorder.prepare();
                mRecorder.start();
                isRecording = true; // 更新状态标记
            } catch (IOException e) {
                e.printStackTrace();
                cleanUpRecorder();
            } catch(IllegalStateException e ){
                e.printStackTrace();
                cleanUpRecorder();
            }
        } catch (RuntimeException | IOException e) {
            e.printStackTrace();
        }
    }
}

// 封装统一的清理逻辑,避免重复代码
private void cleanUpRecorder() {
    if (mRecorder != null) {
        try {
            mRecorder.stop();
        } catch (RuntimeException ignored) {}
        mRecorder.release();
        mRecorder = null;
    }
    isRecording = false;
}

public void stop() {
    cleanUpRecorder();
}

二、错误码-38的针对性修复

错误码-38的常见触发场景及解决办法:

  • 输出路径不兼容:部分Android设备不允许MediaRecorder写入/dev/null,换成应用私有目录(如getCacheDir())下的临时文件即可解决;
  • 格式组合不匹配:AMR_WB属于高码率格式,部分低端设备不支持,换成THREE_GPP+AMR_NB的通用组合适配性更强;
  • 权限未正确获取:务必确认已申请RECORD_AUDIO权限,Android 6.0+必须动态申请:
    // 动态权限检查示例
    if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) 
            != PackageManager.PERMISSION_GRANTED) {
        ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.RECORD_AUDIO}, 100);
    }
    

三、适配你的周期性录制需求

针对“每15分钟运行一次、持续3秒测最大振幅”的需求,建议用WorkManager实现周期性调度,确保每次录制都严格遵循“初始化→启动→录制3秒→停止清理”的完整流程,避免资源泄漏:

// 用Handler实现3秒后自动停止录制
private Handler mHandler = new Handler(Looper.getMainLooper());

public void startRecordingFor3Seconds() {
    start();
    mHandler.postDelayed(() -> {
        // 停止前获取最大振幅
        int maxAmplitude = mRecorder != null ? mRecorder.getMaxAmplitude() : 0;
        stop();
        // 这里处理最大振幅数据(如存储或上报)
    }, 3000);
}

// 每15分钟触发一次录制,用WorkManager的周期性任务
PeriodicWorkRequest recordingRequest = new PeriodicWorkRequest.Builder(RecordingWorker.class, 15, TimeUnit.MINUTES)
        .build();
WorkManager.getInstance(this).enqueue(recordingRequest);

// 自定义Worker类处理后台录制
public static class RecordingWorker extends Worker {
    public RecordingWorker(@NonNull Context context, @NonNull WorkerParameters params) {
        super(context, params);
    }

    @NonNull
    @Override
    public Result doWork() {
        SoundMeter soundMeter = new SoundMeter(getApplicationContext());
        soundMeter.start();
        try {
            Thread.sleep(3000);
            int maxAmplitude = soundMeter.getMaxAmplitude();
            // 处理最大振幅数据
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            soundMeter.stop();
        }
        return Result.success();
    }
}

最后排查步骤

  1. 优先确认RECORD_AUDIO权限是否已正确授予(包括动态权限申请);
  2. 替换输出文件路径为应用私有临时文件;
  3. 检查是否存在重复调用start()的情况,用状态标记管控;
  4. 确保stop()方法彻底释放资源,避免残留状态影响下一次录制。

内容的提问来源于stack exchange,提问作者Jakub Gabčo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 07:47:00