You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

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

火山引擎 最新活动