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(); } }
最后排查步骤
- 优先确认
RECORD_AUDIO权限是否已正确授予(包括动态权限申请); - 替换输出文件路径为应用私有临时文件;
- 检查是否存在重复调用
start()的情况,用状态标记管控; - 确保
stop()方法彻底释放资源,避免残留状态影响下一次录制。
内容的提问来源于stack exchange,提问作者Jakub Gabčo
相关产品推荐
相关产品推荐

