Android API26版本来电录音抛出IllegalStateException问题求助
Android API 26+ 来电录音抛出 IllegalStateException (start failed: -38) 解决方案
我来帮你分析这个问题——你遇到的start failed: -38错误在API26+出现,核心原因是Android 8.0(API26)开始对音频录制的权限、后台执行逻辑以及音频源的使用做了更严格的限制。结合你的代码,我们可以一步步修复:
1. 替换适配API26+的音频源
API26之后,MediaRecorder.AudioSource.VOICE_CALL被列为系统级权限,普通第三方应用无法直接调用;而你当前用的MIC音频源,在来电状态下会被系统通话服务占用,导致录制启动失败。
建议改用VOICE_COMMUNICATION,这个音频源是专门为语音通话场景设计的,API21+支持,且不需要系统级权限:
if (Build.VERSION.SDK_INT <= 22) { myAudioRecorder.setAudioSource(MediaRecorder.AudioSource.VOICE_CALL); } else { // 替换为适配API26+的音频源 myAudioRecorder.setAudioSource(MediaRecorder.AudioSource.VOICE_COMMUNICATION); }
2. 确保完整的权限申请
API23+开始,录音权限是危险权限,必须动态申请;API26+后台录音还需要规避系统的后台限制:
- 动态申请RECORD_AUDIO权限:在启动录音前必须检查权限,未授权时弹出申请框:
private static final int REQUEST_RECORD_PERMISSION = 100; // 调用startRecording前先检查权限 if (ContextCompat.checkSelfPermission(context, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions((Activity) context, new String[]{Manifest.permission.RECORD_AUDIO}, REQUEST_RECORD_PERMISSION); // 等待用户授权后再执行录音逻辑 return; }
- 别忘了在
AndroidManifest.xml中声明权限:
<uses-permission android:name="android.permission.RECORD_AUDIO" /> <!-- API29+需要额外声明 --> <uses-permission android:name="android.permission.ACCESS_MEDIA_LOCATION" />
3. 修正MediaRecorder的配置顺序
你的代码里把setOnErrorListener放在了start()之后,这会导致启动时的错误无法被监听到。正确的顺序应该是先设置监听器,再配置参数:
myAudioRecorder = new MediaRecorder(); myAudioRecorder.reset(); // 先设置错误监听器,确保能捕获所有阶段的错误 myAudioRecorder.setOnErrorListener(errorListener); // 再配置音频源、输出格式等参数 myAudioRecorder.setAudioSource(...); myAudioRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); myAudioRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); fileName = outputPath + "/" + Filename(); myAudioRecorder.setOutputFile(fileName); try { myAudioRecorder.prepare(); myAudioRecorder.start(); } catch (Exception e){ throw new Exception(e.getMessage()); }
4. 用前台服务规避API26+后台限制
Android 8.0开始,后台应用启动MediaRecorder会被系统限制。当来电触发PhoneListener.onCallStateChanged时,你的应用大概率处于后台状态,此时需要用前台服务来执行录音:
- 创建一个
CallRecorderService,在服务中执行录音逻辑,并启动前台通知:
public class CallRecorderService extends Service { private static final String CHANNEL_ID = "call_recorder_channel"; private static final int NOTIFICATION_ID = 101; private MediaRecorder myAudioRecorder; @Override public int onStartCommand(Intent intent, int flags, int startId) { // 创建前台通知,避免被系统杀死 createNotificationChannel(); Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID) .setContentTitle("通话录音中") .setContentText("正在录制当前通话") .setSmallIcon(R.drawable.ic_recording) .build(); startForeground(NOTIFICATION_ID, notification); // 执行录音逻辑(把你的startRecording代码移到这里) String outputPath = intent.getStringExtra("outputPath"); try { startRecording(outputPath); } catch (Exception e) { e.printStackTrace(); stopSelf(); } return START_STICKY; } private void createNotificationChannel() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationChannel channel = new NotificationChannel(CHANNEL_ID, "通话录音通知", NotificationManager.IMPORTANCE_LOW); NotificationManager manager = getSystemService(NotificationManager.class); manager.createNotificationChannel(channel); } } // 你的startRecording方法修改后放在这里 private void startRecording(String outputPath) throws Exception { // ... 你的配置代码 } @Nullable @Override public IBinder onBind(Intent intent) { return null; } }
- 在
PhoneListener.onCallStateChanged中启动前台服务:
Intent serviceIntent = new Intent(context, CallRecorderService.class); serviceIntent.putExtra("outputPath", outputPath); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { context.startForegroundService(serviceIntent); } else { context.startService(serviceIntent); }
5. 增强错误排查
在错误监听器中打印详细错误码,帮助定位问题:
MediaRecorder.OnErrorListener errorListener = new MediaRecorder.OnErrorListener() { @Override public void onError(MediaRecorder mr, int what, int extra) { Log.e("CallRecorder", "MediaRecorder错误: what=" + what + ", extra=" + extra); // 常见错误码:what=1(硬件错误),extra=-38(状态错误/资源被占用) } };
按照以上步骤修改后,API26+的来电录音问题应该就能解决了。
内容的提问来源于stack exchange,提问作者MR.asadzadeh




