如何以编程方式获取通话时长?及按条件结束呼出电话的实现咨询
Hey there! Let's tackle your two Android telephony development questions one by one:
1. 如何通过编程方式获取通话过程中的通话时长
获取通话时长主要有两种实用方案,你可以根据场景选择:
方案一:实时监听通话状态计算时长
通过监听电话状态变化,记录通话接通和结束的时间戳,实时计算时长:
步骤说明:
- 注册
BroadcastReceiver监听android.intent.action.PHONE_STATE动作 - 当状态变为
TelephonyManager.CALL_STATE_OFFHOOK(通话接通)时,记录当前时间 - 当状态变为
TelephonyManager.CALL_STATE_IDLE(通话结束)时,计算时间差得到通话时长 - 注意:Android 10+ 需要申请
READ_PHONE_STATE权限(动态申请);Android 12+ 还需POST_NOTIFICATIONS权限保障广播正常工作
示例代码:
public class CallDurationReceiver extends BroadcastReceiver { private long callStartTime = 0; private boolean isCallActive = false; @Override public void onReceive(Context context, Intent intent) { if (!intent.getAction().equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) { return; } String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE); switch (state) { case TelephonyManager.EXTRA_STATE_OFFHOOK: // 通话接通,启动计时 callStartTime = System.currentTimeMillis(); isCallActive = true; break; case TelephonyManager.EXTRA_STATE_IDLE: if (isCallActive) { // 通话结束,计算时长 long durationInMillis = System.currentTimeMillis() - callStartTime; long durationInSeconds = durationInMillis / 1000; Log.d("CallDuration", "当前通话时长:" + durationInSeconds + "秒"); isCallActive = false; } break; } } }
方案二:读取系统通话记录
如果需要获取历史通话时长,可以直接读取系统通话记录,需要READ_CALL_LOG权限:
示例代码片段:
ContentResolver resolver = getContentResolver(); Cursor cursor = resolver.query( CallLog.Calls.CONTENT_URI, new String[]{CallLog.Calls.DURATION}, CallLog.Calls.NUMBER + " = ?", new String[]{"目标号码"}, CallLog.Calls.DATE + " DESC" ); if (cursor != null && cursor.moveToFirst()) { long duration = cursor.getLong(cursor.getColumnIndexOrThrow(CallLog.Calls.DURATION)); Log.d("CallLog", "历史通话时长:" + duration + "秒"); cursor.close(); }
2. 完善可根据特定条件结束呼出电话的应用
从你给出的代码片段来看,已经在监听电话状态了,接下来我们补充完整逻辑,实现「根据时长、白名单条件结束呼出电话」的功能:
第一步:补充权限与广播配置
除了监听PHONE_STATE,还需要监听NEW_OUTGOING_CALL捕获呼出事件,同时在清单中声明必要权限:
权限声明(AndroidManifest.xml):
<uses-permission android:name="android.permission.READ_PHONE_STATE" /> <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" /> <uses-permission android:name="android.permission.ANSWER_PHONE_CALLS" /> <!-- Android 10+ 额外需要 --> <uses-permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY" />
注册广播接收器:
<receiver android:name=".CallControlReceiver" android:enabled="true" 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>
第二步:完善接收器核心逻辑
我们需要记录呼出号码、通话开始时间,再结合白名单和时长条件判断是否结束通话:
public class CallControlReceiver extends BroadcastReceiver { private long outgoingCallStartTime = 0; private String currentOutgoingNumber = ""; private Context appContext; @Override public void onReceive(Context context, Intent intent) { appContext = context.getApplicationContext(); String action = intent.getAction(); if (TelephonyManager.ACTION_NEW_OUTGOING_CALL.equals(action)) { // 捕获呼出电话,记录目标号码 currentOutgoingNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER); Log.d("CallControl", "呼出号码:" + currentOutgoingNumber); } else if (TelephonyManager.ACTION_PHONE_STATE_CHANGED.equals(action)) { String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE); switch (state) { case TelephonyManager.EXTRA_STATE_OFFHOOK: // 呼出电话接通,启动计时检查 if (!currentOutgoingNumber.isEmpty()) { outgoingCallStartTime = System.currentTimeMillis(); startDurationCheck(); } break; case TelephonyManager.EXTRA_STATE_IDLE: // 通话结束,重置状态 currentOutgoingNumber = ""; outgoingCallStartTime = 0; break; } } } private void startDurationCheck() { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(appContext); long delayMillis = Long.parseLong(prefs.getString("timedelay", "0")) * 1000; if (delayMillis <= 0) return; new Handler(Looper.getMainLooper()).postDelayed(() -> { // 查询白名单,判断是否需要结束通话 WhitelistDAO whiteListDao = new WhitelistDAO(appContext); List<Whitelist> whiteList = whiteListDao.getAllWhitelistItems(); // 假设你有该查询方法 boolean isInWhitelist = whiteList.stream() .anyMatch(item -> item.getNumber().equals(currentOutgoingNumber)); if (!isInWhitelist) { endCurrentCall(); } }, delayMillis); } private void endCurrentCall() { // 适配不同Android版本的通话结束API if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { TelecomManager telecomManager = (TelecomManager) appContext.getSystemService(Context.TELECOM_SERVICE); if (telecomManager != null) { try { telecomManager.endCall(); Log.d("CallControl", "已自动结束呼出电话"); } catch (SecurityException e) { Log.e("CallControl", "结束通话失败:权限不足", e); } } } else { // Android 9及以下用反射调用废弃的endCall方法 TelephonyManager telephonyManager = (TelephonyManager) appContext.getSystemService(Context.TELEPHONY_SERVICE); try { Method endCallMethod = telephonyManager.getClass().getMethod("endCall"); endCallMethod.invoke(telephonyManager); Log.d("CallControl", "已自动结束呼出电话"); } catch (Exception e) { Log.e("CallControl", "结束通话失败", e); } } } }
关键注意事项:
- 动态权限申请:Android 6+ 必须动态申请
READ_PHONE_STATE、PROCESS_OUTGOING_CALLS、ANSWER_PHONE_CALLS权限 - 厂商兼容性:部分国产厂商对通话控制功能有额外限制,需在目标设备上测试验证
- 白名单DAO完善:确保你的
WhitelistDAO能正确从数据库读取白名单数据
内容的提问来源于stack exchange,提问作者Dillz




