如何在双卡设备上使用PhoneStateListener监听通话状态?
解决双SIM卡设备中SIM1通话状态异常触发的问题
嘿,这个问题在双SIM卡Android设备上真的挺常见的,我帮你分析下原因并给出可行的解决方案~
问题根源
你的PhoneStateListener是绑定到默认的TelephonyManager实例上的,而很多双SIM卡设备对主卡(通常是SIM1)的通话状态通知逻辑和副卡(SIM2)不一样:系统可能在SIM1通话建立时快速触发一次CALL_STATE_IDLE的状态波动,或者默认的TelephonyManager没有正确区分双卡的独立通话状态,导致你刚收到OFFHOOK就马上收到IDLE,直接终止了录音。
解决方案
要解决这个问题,核心是针对每个SIM卡独立注册通话状态监听,同时加入状态防抖逻辑避免误触发。
步骤1:适配双卡监听API(Android 5.1+)
从Android 5.1(API 22)开始,系统提供了SubscriptionManager来管理双卡的订阅信息,我们可以为每个SIM卡创建独立的PhoneStateListener,绑定到对应的SIM卡订阅ID上。
步骤2:修改代码实现
首先,获取所有活跃的SIM卡订阅ID,为每个ID注册专属的监听:
// 在初始化的地方获取双卡信息并注册监听 SubscriptionManager subscriptionManager = (SubscriptionManager) getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE); List<SubscriptionInfo> activeSubscriptions = subscriptionManager.getActiveSubscriptionInfoList(); if (activeSubscriptions != null) { for (SubscriptionInfo info : activeSubscriptions) { int subId = info.getSubscriptionId(); TelephonyManager telephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); // 为每个SIM卡注册独立的监听 telephonyManager.listen(new SimSpecificPhoneStateListener(subId), PhoneStateListener.LISTEN_CALL_STATE); } }
然后,自定义PhoneStateListener来管理每个SIM卡的录音状态,同时加入延迟防抖:
// 用Map来分别记录每个SIM卡的录音状态和通话日志 private Map<Integer, AtomicBoolean> simRecordingStates = new HashMap<>(); private Map<Integer, CallLog> simCallLogs = new HashMap<>(); private Context context; // 确保你已经初始化了context private class SimSpecificPhoneStateListener extends PhoneStateListener { private int subId; public SimSpecificPhoneStateListener(int subId) { this.subId = subId; // 初始化当前SIM卡的状态 simRecordingStates.putIfAbsent(subId, new AtomicBoolean(false)); simCallLogs.putIfAbsent(subId, null); } @Override public void onCallStateChanged(int state, String incomingNumber) { super.onCallStateChanged(state, incomingNumber); AtomicBoolean isRecording = simRecordingStates.get(subId); CallLog phoneCall = simCallLogs.get(subId); switch (state) { case TelephonyManager.CALL_STATE_IDLE: if (isRecording.get()) { RecordCallService.stopRecording(context); simCallLogs.put(subId, null); isRecording.set(false); } break; case TelephonyManager.CALL_STATE_OFFHOOK: // 加1秒延迟防抖,避免系统状态波动导致误触发 new Handler(Looper.getMainLooper()).postDelayed(() -> { // 再次确认当前SIM卡的通话状态是否真的是OFFHOOK TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) { if (tm.getCallStateForSubscription(subId) == TelephonyManager.CALL_STATE_OFFHOOK && !isRecording.get()) { RecordCallService.startRecording(context, phoneCall); isRecording.set(true); } } }, 1000); // 延迟时间可以根据你的设备调整,500ms-1500ms都可以 break; case TelephonyManager.CALL_STATE_RINGING: if (phoneCall == null) { phoneCall = new CallLog(); simCallLogs.put(subId, phoneCall); } break; } } }
额外注意事项
- 权限检查:确保你已经申请了
READ_PHONE_STATE、RECORD_AUDIO、READ_CALL_LOG这些必要权限,Android 10及以上版本还需要适配通话录音的系统限制(比如使用MediaProjection或者针对特定厂商的API)。 - 版本适配:
getCallStateForSubscription是API 22才有的方法,记得在代码中加入版本判断,避免低版本设备崩溃。 - 状态清理:当应用退到后台或者销毁时,记得注销所有注册的
PhoneStateListener,避免内存泄漏。
这样修改后,每个SIM卡的通话状态都会被独立监听,再加上延迟防抖的逻辑,就能解决SIM1上IDLE状态异常触发的问题啦~
内容的提问来源于stack exchange,提问作者priojeet priyom




