Android通话录音服务问题:重复生成音频文件+重启后无法录音
针对通话状态监听与录制问题的解决方案
先帮你拆解代码里的两个核心问题,给出针对性的修复方案:
问题1:日志重复输出、同一通话生成多个音频文件
问题根源
- Broadcast Receiver重复注册:你的
onStartCommand虽判断了br_call == null才注册,但如果Service因系统资源紧张被重启,br_call的引用可能还存在但Receiver已被系统注销,导致重复注册,进而多次接收同一广播事件。 - 通话状态判断逻辑不严谨:
TelephonyManager的通话状态广播(比如EXTRA_STATE_RINGING)可能被系统多次发送,你的代码没做状态变化校验,会重复触发日志和录制逻辑。 - 录制初始化遗漏判重:当
EXTRA_STATE_OFFHOOK多次触发时,你没判断recorder是否已处于录制状态,会重复创建音频文件和MediaRecorder实例。
修复方案
第一步:确保Receiver唯一注册与注销
在Service的onDestroy方法中强制注销Receiver,避免内存泄漏和重复注册:
@Override public void onDestroy() { super.onDestroy(); if (br_call != null) { try { unregisterReceiver(br_call); } catch (IllegalArgumentException e) { // 防止Receiver未注册时抛出异常 e.printStackTrace(); } br_call = null; } // 同时释放录制资源 if (recorder != null) { try { recorder.stop(); } catch (RuntimeException e) { e.printStackTrace(); } recorder.release(); recorder = null; } recordstarted = false; wasRinging = false; }
第二步:优化通话状态判断逻辑
添加状态前后对比,只处理状态变化的事件,避免重复触发:
// 在CallBr类中添加成员变量记录上一次状态 private String lastState = TelephonyManager.EXTRA_STATE_IDLE; @Override public void onReceive(Context context, Intent intent) { if (intent.getAction().equals(ACTION_IN)) { Bundle bundle = intent.getExtras(); if (bundle == null) return; String currentState = bundle.getString(TelephonyManager.EXTRA_STATE); // 仅当状态发生变化时才处理 if (currentState.equals(lastState)) return; lastState = currentState; Log.d("tag", "state ::" + currentState); if (currentState.equals(TelephonyManager.EXTRA_STATE_RINGING)) { if (!wasRinging) { inCall = bundle.getString(TelephonyManager.EXTRA_INCOMING_NUMBER); wasRinging = true; Toast.makeText(context, "IN : " + inCall, Toast.LENGTH_LONG).show(); } } else if (currentState.equals(TelephonyManager.EXTRA_STATE_OFFHOOK)) { if (wasRinging && !recordstarted) { // 仅当未开始录制时才初始化 Toast.makeText(context, "ANSWERED", Toast.LENGTH_LONG).show(); // 保留你原有的录制初始化代码即可 } } else if (currentState.equals(TelephonyManager.EXTRA_STATE_IDLE)) { wasRinging = false; Toast.makeText(context, "REJECT || DISCONNECT", Toast.LENGTH_LONG).show(); if (recordstarted) { try { recorder.stop(); recordstarted = false; } catch (RuntimeException e) { e.printStackTrace(); } } } } // 呼出电话逻辑保持不变 }
第三步:避免重复创建音频文件
将audiofile设为CallBr的成员变量,创建前判断是否已存在,同时添加时间戳避免文件名冲突:
// 在CallBr类中添加成员变量 private File audiofile; // 录制初始化部分修改为: if (audiofile == null) { File sampleDir = new File(Environment.getExternalStorageDirectory(), "/RecordingDemo"); if (!sampleDir.exists()) { sampleDir.mkdirs(); } // 添加时间戳确保文件名唯一 String file_name = inCall + "_" + System.currentTimeMillis(); try { audiofile = File.createTempFile(file_name, ".amr", sampleDir); } catch (IOException e) { e.printStackTrace(); } } // 后续录制逻辑直接使用已创建的audiofile
问题2:杀死进程后重启应用无法录制
问题根源
你用的是动态注册Broadcast Receiver,这种注册方式的Receiver依赖进程存活。当应用进程被杀死后,动态注册的Receiver会被系统注销,即使重启应用,如果Service没被重新启动并注册Receiver,就无法监听通话状态。
修复方案
方案1:将Broadcast Receiver改为静态注册
在AndroidManifest.xml中静态注册Receiver,确保即使进程未启动,系统也能唤醒应用处理通话广播:
<receiver android:name=".CallBr" android:exported="true"> <intent-filter> <action android:name="android.intent.action.PHONE_STATE"/> <action android:name="android.intent.action.NEW_OUTGOING_CALL"/> </intent-filter> </receiver>
注:Android 8.0及以上对隐式广播有限制,但
PHONE_STATE和NEW_OUTGOING_CALL属于例外广播,可正常接收。
方案2:确保Service自动重启并保持存活
- 在
onStartCommand中返回START_STICKY,让系统在Service被杀死后尝试重启:
@Override public int onStartCommand(Intent intent, int flags, int startId) { // 原有注册Receiver逻辑 return START_STICKY; // 替换原来的super返回值 }
- 将Service设置为前台服务,降低被系统杀死的概率:
@Override public void onCreate() { super.onCreate(); // 创建前台通知(Android 8.0及以上需要通知渠道) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationChannel channel = new NotificationChannel("call_recorder", "通话录制服务", NotificationManager.IMPORTANCE_LOW); NotificationManager manager = getSystemService(NotificationManager.class); manager.createNotificationChannel(channel); } Notification notification = new NotificationCompat.Builder(this, "call_recorder") .setContentTitle("通话录制服务运行中") .setSmallIcon(R.drawable.ic_notification) .build(); startForeground(1, notification); }
- 在应用启动时(比如MainActivity的
onCreate)启动Service,确保进程重启后Service能被重新拉起:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 启动通话录制服务 Intent serviceIntent = new Intent(this, YourService.class); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { startForegroundService(serviceIntent); } else { startService(serviceIntent); } }
内容的提问来源于stack exchange,提问作者Hitesh Raviya
相关产品推荐
相关产品推荐

