You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android通话录音服务问题:重复生成音频文件+重启后无法录音

针对通话状态监听与录制问题的解决方案

先帮你拆解代码里的两个核心问题,给出针对性的修复方案:


问题1:日志重复输出、同一通话生成多个音频文件

问题根源

  1. Broadcast Receiver重复注册:你的onStartCommand虽判断了br_call == null才注册,但如果Service因系统资源紧张被重启,br_call的引用可能还存在但Receiver已被系统注销,导致重复注册,进而多次接收同一广播事件。
  2. 通话状态判断逻辑不严谨:TelephonyManager的通话状态广播(比如EXTRA_STATE_RINGING)可能被系统多次发送,你的代码没做状态变化校验,会重复触发日志和录制逻辑。
  3. 录制初始化遗漏判重:当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自动重启并保持存活

  1. 在onStartCommand中返回START_STICKY,让系统在Service被杀死后尝试重启:
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    // 原有注册Receiver逻辑
    return START_STICKY; // 替换原来的super返回值
}
  1. 将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);
}
  1. 在应用启动时(比如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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 09:56:22