Android MediaRecorder通过BroadcastReceiver触发后台录制无音频问题求助
问题分析与解决方案
从你的描述来看,录制逻辑本身是没问题的(在MainActivity、AccessibilityEvent触发或重启应用时都能正常工作),核心问题出在从BroadcastReceiver触发录制时的音频上下文与权限/资源抢占上。以下是具体的分析和修复方案:
核心原因
当通话进入OFFHOOK状态时,系统会优先把音频资源分配给通话进程,此时直接通过BroadcastReceiver触发录制,可能存在三个关键问题:
- 音频焦点未正确获取,MediaRecorder无法捕获通话音频;
- 服务启动时机过早,通话音频系统尚未完成初始化,导致录制源无输入;
- 静态BroadcastReceiver在Android 8+的限制下,可能无法及时与后台服务交互,导致录制初始化异常。
解决方案一:优化音频焦点与录制源配置
修改TService中的startRecording方法,适配通话场景的音频模式与焦点请求:
public void startRecording() { if (audioRecorder != null) return; Log.i(TAG, "startRecord"); Calendar calendar = Calendar.getInstance(); long time = System.currentTimeMillis(); String str = getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS) + "/D" + calendar.get(Calendar.DAY_OF_MONTH) + "M" + calendar.get(Calendar.MONTH) + "T" + time + ".3gp"; // 1. 配置音频管理器,切换到通话模式 AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); audioManager.setMode(AudioManager.MODE_IN_CALL); // 2. 请求音频焦点,确保录制能获取音频资源 AudioAttributes attrs = new AudioAttributes.Builder() .setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION) .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH) .build(); AudioFocusRequest focusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT) .setAudioAttributes(attrs) .setAcceptsDelayedFocusGain(true) .build(); int focusResult = audioManager.requestAudioFocus(focusRequest); if (focusResult != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { Log.e(TAG, "Failed to get audio focus"); return; } // 3. 初始化MediaRecorder,使用通话专用音频源 MediaRecorder recorder = new MediaRecorder(); recorder.setOnErrorListener(errorListener); // 替换MIC为VOICE_COMMUNICATION,适配通话场景(Android 7+推荐) recorder.setAudioSource(MediaRecorder.AudioSource.VOICE_COMMUNICATION); recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); recorder.setOutputFile(str); recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); try { recorder.prepare(); recorder.start(); audioRecorder = recorder; Log.i(TAG, "Recording started successfully"); } catch (IOException e) { Log.e(TAG, "prepare() failed: " + e.getMessage()); audioManager.abandonAudioFocusRequest(focusRequest); } }
解决方案二:简化服务启动流程,避免广播中转
你的当前流程是:CallBr发送广播 → TService内部Receiver接收 → 启动前台服务。这个中转环节可能导致时序问题,直接在CallBr中启动TService更可靠:
修改CallBr的EXTRA_STATE_OFFHOOK分支:
if (state.equals(TelephonyManager.EXTRA_STATE_OFFHOOK)) { Log.i(TAG, "EXTRA_STATE_OFFHOOK"); Intent serviceIntent = new Intent(context, TService.class); serviceIntent.putExtra("action", "startRecording"); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { context.startForegroundService(serviceIntent); } else { context.startService(serviceIntent); } }
同时修改TService的onStartCommand方法,直接处理启动指令:
@Override public int onStartCommand(Intent intent, int flags, int startId) { String action = intent.getStringExtra("action"); if ("startRecording".equals(action)) { createNotification(); // 延迟1.5秒启动录制,给通话音频系统足够初始化时间 new Handler(Looper.getMainLooper()).postDelayed(this::startRecording, 1500); } else if ("stopRecording".equals(action)) { stopRecording(); stopForeground(STOP_FOREGROUND_REMOVE); } return START_STICKY; // 改用START_STICKY确保服务意外终止后重启 }
解决方案三:确保服务长期运行与权限适配
- 在
AndroidManifest.xml中,给TService添加前台服务权限(针对Android 12+):
<service android:name=".TService" android:permission="android.permission.FOREGROUND_SERVICE_MICROPHONE" android:foregroundServiceType="microphone"> <intent-filter> <action android:name="android.accessibilityservice.AccessibilityService" /> </intent-filter> </service>
- 确保
TService在开机后自动启动,避免广播触发时服务未运行:
可以在CallBr中添加开机广播监听,或者在MainActivity启动时主动启动TService(作为前台服务)。
额外注意事项
- Android版本差异:Android 10及以上对通话录制有更严格的限制,部分设备可能需要用户手动开启"通话录音"权限,或者只能通过辅助服务实现(这也是你用AccessibilityEvent触发正常的原因);
- 音频源选择:如果
VOICE_COMMUNICATION无效,可以尝试MediaRecorder.AudioSource.VOICE_CALL(仅Android 6及以下可用); - 日志排查:在
startRecording中添加更多日志,比如打印audioManager.getMode()、音频焦点请求结果,帮助定位问题。
内容的提问来源于stack exchange,提问作者phalangesgnome
相关产品推荐
相关产品推荐

