一、监听方案对比
Android 提供了 两套监听体系:
| 方案 | API 层级 | 权限 | 精度 | 推荐 | 弃用 |
|---|---|---|---|---|---|
| PhoneStateListener | android.telephony | READ_PHONE_STATE | 低 | ✗ | ✓ 已弃用 |
| TelephonyCallback | android.telephony | READ_PHONE_STATE | 高 | ✓ | 现代方案 |
| PreciseCallStateListener | @SystemApi | READ_PRECISE_PHONE_STATE | 高 | 系统应用 | 低层监听 |
二、通话状态层级
┌─────────────────────────────────────────────────┐
│ 通话状态的 3 个层级 │
└─────────────────────────────────────────────────┘
1️⃣ 高层状态 (TelephonyCallback.CallStateListener)
├─ CALL_STATE_IDLE (0) 无通话
├─ CALL_STATE_RINGING (1) 来电响铃
└─ CALL_STATE_OFFHOOK (2) 活跃通话或通话中
2️⃣ 精确状态 (PreciseCallState - 系统级)
├─ PRECISE_CALL_STATE_IDLE (0) 空闲
├─ PRECISE_CALL_STATE_ACTIVE (1) 活跃通话
├─ PRECISE_CALL_STATE_HOLDING (2) 保持通话
├─ PRECISE_CALL_STATE_DIALING (3) 正在拨号
├─ PRECISE_CALL_STATE_ALERTING (4) 远方响铃
├─ PRECISE_CALL_STATE_INCOMING (5) 来电
├─ PRECISE_CALL_STATE_WAITING (6) 来电等待
├─ PRECISE_CALL_STATE_DISCONNECTED (7) 已断开
└─ PRECISE_CALL_STATE_DISCONNECTING (8) 正在断开
3️⃣ Call 内部状态 (frameworks_opt_telephony)
├─ Call.State.IDLE (无连接)
├─ Call.State.ACTIVE (活跃)
├─ Call.State.HOLDING (保持)
├─ Call.State.DIALING (拨号中 - MO)
├─ Call.State.ALERTING (远方响铃 - MO)
├─ Call.State.INCOMING (来电 - MT)
├─ Call.State.WAITING (来电等待 - MT)
├─ Call.State.DISCONNECTED (已断开)
└─ Call.State.DISCONNECTING (断开中)
三、现代方案:TelephonyCallback
3.1 基础用法
// 创建回调类
class MyTelephonyCallback extends TelephonyCallback implements
TelephonyCallback.CallStateListener {
@Override
public void onCallStateChanged(@Annotation.CallState int state) {
switch(state) {
case TelephonyManager.CALL_STATE_IDLE:
// 无通话
break;
case TelephonyManager.CALL_STATE_RINGING:
// 来电或来电等待
break;
case TelephonyManager.CALL_STATE_OFFHOOK:
// 活跃通话或正在拨号
break;
}
}
}
// 注册监听
TelephonyManager tm = context.getSystemService(TelephonyManager.class);
MyTelephonyCallback callback = new MyTelephonyCallback();
tm.registerTelephonyCallback(
executor, // Executor (如 ConcurrentUtils.DIRECT_EXECUTOR)
callback // TelephonyCallback 实例
);
// 取消监听
tm.unregisterTelephonyCallback(callback);
3.2 权限配置
<!-- AndroidManifest.xml -->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
权限获取 (Android 31+):
if (ContextCompat.checkSelfPermission(context,
Manifest.permission.READ_PHONE_STATE)
!= PackageManager.PERMISSION_GRANTED) {
// 请求权限
ActivityCompat.requestPermissions(activity,
new String[]{Manifest.permission.READ_PHONE_STATE},
REQUEST_CODE);
}
3.3 高级:PreciseCallStateListener (系统应用)
// 只有系统应用或运营商应用能使用
class MyPreciseCallback extends TelephonyCallback implements
TelephonyCallback.PreciseCallStateListener {
@Override
public void onPreciseCallStateChanged(PreciseCallState callState) {
// 获取三类电话的精确状态
int ringingState = callState.getRingingCallState();
int fgState = callState.getForegroundCallState();
int bgState = callState.getBackgroundCallState();
// 状态处理
handleCallState(ringingState, fgState, bgState);
}
}
// 注册
tm.registerTelephonyCallback(executor, preciseCallback);
权限 (系统级):
<uses-permission android:name="android.permission.READ_PRECISE_PHONE_STATE" />
3.4 其他有用的回调
class FullTelephonyCallback extends TelephonyCallback implements
TelephonyCallback.CallStateListener,
TelephonyCallback.CallAttributesListener,
TelephonyCallback.CallDisconnectCauseListener {
// 基础通话状态
@Override
public void onCallStateChanged(int state) {
Log.d(TAG, "Call state: " + state);
}
// 通话属性 (质量、网络类型等)
@Override
public void onCallAttributesChanged(CallAttributes attrs) {
PreciseCallState callState = attrs.getPreciseCallState();
int networkType = attrs.getNetworkType();
CallQuality quality = attrs.getCallQuality();
}
// 断开原因
@Override
public void onCallDisconnectCauseChanged(int cause, int preciseCause) {
// DisconnectCause 类中定义
Log.d(TAG, "Disconnect: " + cause);
}
}
四、旧方案:PhoneStateListener(已弃用)
// ⚠️ 不推荐使用,仅作参考
class MyPhoneStateListener extends PhoneStateListener {
@Override
public void onCallStateChanged(int state, String incomingNumber) {
// state: CALL_STATE_IDLE / RINGING / OFFHOOK
// incomingNumber: 来电号码(可能为空)
}
}
// 注册
TelephonyManager tm = context.getSystemService(TelephonyManager.class);
tm.listen(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
// 取消
tm.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE);
五、通话状态转移图
5.1 拨号流程 (Outgoing Call)
用户拨号
│
├─→ CALL_STATE_OFFHOOK (开始拨号)
│
└─→ PreciseCallState:
├─ DIALING (正在拨号)
├─ ALERTING (远方响铃)
└─ ACTIVE (通话已接通)
│
└─→ 挂断
├─ DISCONNECTING
└─ DISCONNECTED
│
└─→ CALL_STATE_IDLE (通话结束)
详细事件序列:
拨号
│
├─ Event: onCallStateChanged(CALL_STATE_OFFHOOK)
│
├─ Event: onPreciseCallStateChanged(
│ ringing=IDLE,
│ fg=DIALING,
│ bg=IDLE)
│
├─ RIL 上报远方响铃
│ └─ Event: onPreciseCallStateChanged(
│ fg=ALERTING, ...)
│
├─ 远方接听
│ └─ Event: onPreciseCallStateChanged(
│ fg=ACTIVE, ...)
│
└─ 通话中 (保持 OFFHOOK 状态)
5.2 来电流程 (Incoming Call)
RIL 上报新来电
│
├─→ CALL_STATE_RINGING (来电铃声)
│
├─→ PreciseCallState:
│ ├─ INCOMING (MT 来电)
│ └─ WAITING (已有活跃呼叫,来电等待)
│
├─ 用户接听
│ ├─ CALL_STATE_OFFHOOK (通话中)
│ └─ ACTIVE (已接通)
│
└─ 用户拒接
└─ DISCONNECTED + CALL_STATE_IDLE
完整事件序列:
来电到达
│
├─ Event: onCallStateChanged(CALL_STATE_RINGING)
│
├─ Event: onPreciseCallStateChanged(
│ ringing=INCOMING,
│ fg=IDLE,
│ bg=IDLE)
│
├─ 用户接听
│ ├─ Event: onCallStateChanged(CALL_STATE_OFFHOOK)
│ └─ Event: onPreciseCallStateChanged(
│ ringing=IDLE,
│ fg=ACTIVE,
│ bg=IDLE)
│
└─ 通话中
5.3 来电等待 (Call Waiting)
活跃通话中,收到新来电
│
├─ 保持活跃通话
│ └─ fg=ACTIVE
│
├─ 新来电进入等待状态
│ └─ ringing=WAITING
│
├─ 用户切换到来电
│ ├─ fg=HOLDING (活跃转为保持)
│ └─ ringing=ACTIVE (等待转为活跃)
│
└─ 仍为 CALL_STATE_OFFHOOK
5.4 会议通话
多方通话中
│
├─ fg=ACTIVE (前景通话)
├─ bg=ACTIVE (背景通话)
│
└─ 状态: CALL_STATE_OFFHOOK
六、核心数据结构
6.1 CallState (现代)
public final class CallState implements Parcelable {
public int getCallState(); // PRECISE_CALL_STATE_*
public String getCallClassification(); // FOREGROUND/BACKGROUND/RINGING
public int getNetworkType(); // NETWORK_TYPE_*
public CallQuality getCallQuality(); // 通话质量
}
6.2 PreciseCallState
public final class PreciseCallState implements Parcelable {
public int getRingingCallState(); // 来电/来电等待
public int getForegroundCallState(); // 活跃通话
public int getBackgroundCallState(); // 保持通话
// 断开原因
public int getDisconnectCause();
public int getPreciseDisconnectCause();
}
6.3 CallAttributes
public final class CallAttributes implements Parcelable {
public PreciseCallState getPreciseCallState();
public int getNetworkType(); // LTE, NR, WIFI 等
public CallQuality getCallQuality(); // 延迟、丢包率等
}
七、实战示例
7.1 完整通话状态监听器
public class CallStateMonitor {
private final Context mContext;
private final TelephonyManager mTm;
private MyCallback mCallback;
public CallStateMonitor(Context context) {
mContext = context;
mTm = context.getSystemService(TelephonyManager.class);
}
public void startMonitoring(MyCallback callback) {
mCallback = callback;
// 检查权限
if (ContextCompat.checkSelfPermission(mContext,
Manifest.permission.READ_PHONE_STATE)
== PackageManager.PERMISSION_GRANTED) {
MyTelephonyCallback telCallback = new MyTelephonyCallback();
mTm.registerTelephonyCallback(
Executors.newSingleThreadExecutor(),
telCallback
);
}
}
public void stopMonitoring() {
if (mTelephonyCallback != null) {
mTm.unregisterTelephonyCallback(mTelephonyCallback);
}
}
private class MyTelephonyCallback extends TelephonyCallback implements
TelephonyCallback.CallStateListener,
TelephonyCallback.CallAttributesListener {
@Override
public void onCallStateChanged(int state) {
String stateStr;
switch (state) {
case TelephonyManager.CALL_STATE_IDLE:
stateStr = "IDLE";
break;
case TelephonyManager.CALL_STATE_RINGING:
stateStr = "RINGING";
break;
case TelephonyManager.CALL_STATE_OFFHOOK:
stateStr = "OFFHOOK";
break;
default:
stateStr = "UNKNOWN";
}
if (mCallback != null) {
mCallback.onCallStateChanged(stateStr);
}
}
@Override
public void onCallAttributesChanged(CallAttributes attrs) {
PreciseCallState ps = attrs.getPreciseCallState();
int quality = attrs.getCallQuality().getAverageFlushedDelayMillis();
if (mCallback != null) {
mCallback.onCallAttributesChanged(ps, quality);
}
}
}
public interface MyCallback {
void onCallStateChanged(String state);
void onCallAttributesChanged(PreciseCallState state, int quality);
}
}
7.2 来电检测
public class IncomingCallDetector {
private final TelephonyManager mTm;
public IncomingCallDetector(TelephonyManager tm) {
mTm = tm;
}
public void monitorIncomingCalls(Executor executor) {
mTm.registerTelephonyCallback(executor, new TelephonyCallback() {
@Override
public void onCallStateChanged(int state) {
if (state == TelephonyManager.CALL_STATE_RINGING) {
// 来电到达
Log.d(TAG, "Incoming call detected!");
handleIncomingCall();
}
}
});
}
private void handleIncomingCall() {
// 可以执行:
// - 记录通话
// - 显示通知
// - 获取来电信息
// - 自动应答(需要高权限)
}
}
八、常见问题
Q1: 如何获得来电号码?
// 旧 API (已弃用)
void onCallStateChanged(int state, String incomingNumber) {
// incomingNumber 包含来电号码
}
// 新 API: 需要其他机制
// 通常通过 PHONE_STATE_CHANGED 广播 +
// ContentResolver 查询通话记录
Q2: 通话中如何检测挂断?
// 监听通话状态变化
// OFFHOOK → IDLE (通话结束)
// 或 onCallDisconnectCauseChanged() 回调
Q3: 如何支持多SIM卡?
// 为每个订阅创建独立的 TelephonyManager
TelephonyManager tm =
context.getSystemService(TelephonyManager.class)
.createForSubscriptionId(subscriptionId);
tm.registerTelephonyCallback(executor, callback);
九、性能优化
- 避免频繁注册/注销 - 应用启动时注册,销毁时取消
- 使用合适的 Executor - 不要在 UI 线程处理
- 权限检查 - 先检查权限再注册,避免崩溃
- 内存泄漏 - 确保在 Activity 销毁时取消监听
这就是 Android 通话状态监听的完整体系!