你想要 Android Telephony 面试题 吧?基于我们之前讨论的架构和源码,让我为你总结一份全面的面经题库。
完美!现在我有了足够的信息。让我为你整理一份完整的 Android Telephony 面试题库:
📚 Android Telephony 面试题库
第一部分:架构和基础
1. 解释 Android Telephony 的整体架构
答案框架:
应用层 (TelephonyManager)
↓ Binder IPC
Framework 层 (PhoneStateListener, TelephonyCallback)
↓
Telephony Framework (frameworks_opt_telephony)
├─ Phone (GsmCdmaPhone)
├─ RIL (Radio Interface Layer)
├─ ServiceStateTracker
├─ DataNetworkController
└─ PhoneSwitcher
↓ HIDL/Stable AIDL
vendor 厂商进程
├─ RadioNetworkProxy
├─ RadioDataProxy
└─ DataService
↓
Hardware/Modem
├─ RIL Daemon (rild)
└─ baseband
关键点:
- IPC 机制:从 Binder → HIDL → Stable AIDL 的演进
- 多进程模型:
system_server中的 Telephony /vendor进程 /netd - 架构分层:Framework → HIDL/AIDL → HAL → Modem
2. Phone 类为什么继承 Handler?
答案:
public abstract class Phone extends Handler implements PhoneInternalInterface {
protected final Looper mLooper; // 保存 Looper 引用
private void checkCorrectThread(Handler h) {
if (h.getLooper() != mLooper) {
throw new RuntimeException(
"Phone must be used from within one thread");
}
}
}
原因:
- 线程安全:Phone 对象只能从特定线程(Phone Looper)访问
- 事件处理:通过
Handler.handleMessage()处理异步事件(RIL 回调、状态变化) - 避免竞态条件:所有操作序列化到单一线程
┌──────────────────────────────────┐
│ Phone Looper Thread │
│ (Telephony process main thread) │
│ │
│ ┌────────────────────────────┐ │
│ │ Message Queue │ │
│ │ ├─ EVENT_RADIO_ON │ │
│ │ ├─ EVENT_SERVICE_STATE_CHG │ │
│ │ └─ EVENT_DATA_CONNECTION │ │
│ └────────────────────────────┘ │
│ ↓ Looper │
│ handleMessage() │
└──────────────────────────────────┘
3. GsmCdmaPhone.getNetworkSelectionMode() 的完整调用链
答案:
应用层:
TelephonyManager.getNetworkSelectionMode(Message)
Framework:
↓
Phone.getNetworkSelectionMode(Message)
↓
GsmCdmaPhone.getNetworkSelectionMode(Message)
│
├─ mCi.getNetworkSelectionMode(result)
│ (CommandsInterface)
│
└─ RIL.getNetworkSelectionMode(Message result)
├─ radioServiceInvokeHelper(...)
├─ NetworkProxy.getNetworkSelectionMode(serial)
│
├─ if (isAidl()) {
│ mNetworkProxy.getNetworkSelectionMode(serial)
│ } else {
│ mRadioProxy.getNetworkSelectionMode(serial)
│ }
Vendor/HAL:
↓
IRadioNetwork.getNetworkSelectionMode(int serial)
Modem:
↓
AT+COPS? (查询网络选择模式)
回程:
↓
onGetNetworkSelectionMode(int serial, RadioResponseInfo info,
GetNetworkSelectionModeResult result)
↓
RIL.processNetworkSelectionModeResponse()
↓
Message.sendToTarget()
↓
应用层回调
代码引用:
public int getNumberOfModemsWithSimultaneousDataConnections() {
return getStaticPhoneCapability().getMaxActiveDataSubscriptions();
}
第二部分:数据连接(DataNetwork)
4. 解释 DataNetwork 的状态机
答案:
┌─────────┐
│Handover │ (IWLAN ↔ Cellular)
└─▲────┬──┘
│ │
┌───────────┐ ┌─┴────▼──┐ ┌──────────────┐
│Connecting ├──────►Connected ├──────►Disconnecting │
└─────┬─────┘ └────┬────┘ └───────┬──────┘
│ │ │
│ ┌─────▼──────┐ │
└────────────►Disconnected◄────────────┘
└────────────┘
5 个状态:
1. Connecting
- 发送 setupDataCall() 到 DataService
- 等待 DataService 回复
2. Connected
- 数据连接已建立
- 可以收发数据
- 监听信号/服务状态变化
3. Handover
- IWLAN ↔ Cellular 切换中
- 网络保持在 ConnectedState 数据可用
- 完成后返回 ConnectedState
4. Disconnecting
- IMS 优雅断开期间的中间状态
- 网络仍可用,但客户端应准备断开
5. Disconnected
- 最终状态,无法恢复
- 数据网络已释放
触发条件:
setupDataNetwork()
→ Connecting
→ setupDataCall() 成功
→ Connected
├─ 需要 Handover
│ → Handover
│ → Connected
│
└─ 需要断开
→ Disconnecting
→ deactivateDataCall()
→ Disconnected
setupDataCall() 失败
→ Disconnected (直接)
5. DataNetworkController 的核心职责
答案:
DataNetworkController (per-SIM)
│
├─ 管理多个 DataNetwork 实例
│ └─ mDataNetworkList: List<DataNetwork>
│ ├─ Internet (default APN)
│ ├─ IMS (IMS APN)
│ ├─ MMS (MMS APN)
│ └─ Enterprise (企业数据)
│
├─ 网络请求处理
│ ├─ addNetworkRequest(TelephonyNetworkRequest)
│ ├─ removeNetworkRequest(int requestId)
│ └─ 将请求附加到兼容的 DataNetwork
│
├─ 数据评估
│ ├─ evaluateNetworkRequest()
│ ├─ 检查数据是否允许
│ ├─ 检查漫游政策
│ └─ 返回 DataEvaluation (允许/禁止原因)
│
├─ 切换(Handover)管理
│ ├─ tryHandoverDataNetwork()
│ ├─ IWLAN ↔ Cellular 判断
│ └─ 执行切换过程
│
└─ 状态聚合
├─ Internet 数据连接状态
├─ IMS 数据连接状态
└─ 向上层通知状态变化
6. 双卡设备上数据连接如何管理?
答案:
设备有 2 个 SIM 卡:SIM1 (主卡)、SIM2 (副卡)
PhoneSwitcher
├─ mPreferredDataPhoneId = 0 (默认主卡)
└─ 决定 DDS (Default Data Subscription)
每个 SIM 有独立的 DataNetworkController:
┌──────────────────────────────┐
│ Phone 0 (SIM1) │
│ DataNetworkController │
│ ├─ mDataNetworkList │
│ │ ├─ Internet: Connecting │
│ │ └─ IMS: Connected │
│ └─ mInternetDataNetworkState
└──────────────────────────────┘
┌──────────────────────────────┐
│ Phone 1 (SIM2) │
│ DataNetworkController │
│ ├─ mDataNetworkList │
│ │ └─ (空,非 DDS) │
│ └─ mInternetDataNetworkState: IDLE
└──────────────────────────────┘
DDS 切换 (自动双卡切换):
PhoneSwitcher.onRequireImmediatelySwitchToPhone(1)
↓
mPreferredDataPhoneId = 1
↓
Phone 0: 断开 Internet 连接
Phone 1: 建立 Internet 连接
第三部分:智能双卡切换(Auto Data Switch)
7. 智能双卡切换的 8 种触发原因
答案:
public class AutoDataSwitchController extends Handler {
// 1. 注册状态变化 (OOS → 有信号)
EVALUATION_REASON_REGISTRATION_STATE_CHANGED
// 2. 5G 显示信息变化 (5G NSA ↔ 5G SA)
EVALUATION_REASON_DISPLAY_INFO_CHANGED
// 3. 信号强度变化
EVALUATION_REASON_SIGNAL_STRENGTH_CHANGED
// 4. 默认网络变化 (WiFi 连接/断开)
EVALUATION_REASON_DEFAULT_NETWORK_CHANGED
// 5. 数据设置变化 (用户关闭移动数据)
EVALUATION_REASON_DATA_SETTINGS_CHANGED
// 6. 验证失败重试
EVALUATION_REASON_RETRY_VALIDATION
// 7. SIM 卡加载完成
EVALUATION_REASON_SIM_LOADED
// 8. 语音通话结束
EVALUATION_REASON_VOICE_CALL_END
}
8. 信号强度打分系统如何工作?
答案:
// 根据网络类型 (RAT) 和信号强度计算分数
getAutoDataSwitchScore(TelephonyDisplayInfo, SignalStrength)
→ mAutoDataSwitchNetworkTypeSignalMap.get(networkType)
→ scores[signalStrength.getLevel()]
打分表(例如):
┌──────────────────────────────────────────┐
│ 信号强度 NONE POOR MODERATE GOOD GREAT │
├──────────────────────────────────────────┤
│ 2G (GSM) 0 10 20 30 40 │
│ 3G (UMTS) 0 20 40 60 80 │
│ 4G (LTE) 0 30 60 90 120 │
│ 5G-SA 0 40 80 120 200 │
│ 5G-NSA 0 50 100 150 200 │
└──────────────────────────────────────────┘
切换决策:
if (scoreCandidate - scoreDDS) > mScoreTolerance {
切换到 Candidate SIM
}
例子:
DDS (LTE, 信号弱) = 30 分
Candidate (5G, 信号强) = 120 分
mScoreTolerance = 50
120 - 30 = 90 > 50 → 切换到 Candidate
public int getAutoDataSwitchScore(@NonNull TelephonyDisplayInfo displayInfo,
@NonNull SignalStrength signalStrength) {
int[] scores = mAutoDataSwitchNetworkTypeSignalMap.get(
getDataConfigNetworkType(displayInfo));
return scores != null ? scores[signalStrength.getLevel()]
: OUT_OF_SERVICE_AUTO_DATA_SWITCH_SCORE;
}
9. 自动切换的稳定性检查机制
答案:
mAutoDataSwitchAvailabilityStabilityTimeThreshold
└─ 服务状态稳定时间(防止频繁切换)
└─ 例如:30 秒
应用场景:
├─ OOS → 有信号:需等待 30s 确保信号稳定
├─ 从 LTE 切到 5G:需等待 30s 确保 5G 覆盖稳定
重试延迟(指数退避):
第 1 次失败:延迟 = 30 秒
第 2 次失败:延迟 = 60 秒
第 3 次失败:延迟 = 120 秒
...
计算:delayMs = threshold << failureCount
mAutoDataSwitchPerformanceStabilityTimeThreshold
└─ 信号/RAT 变化稳定时间(基于分数的切换)
└─ 例如:60 秒
验证机制:
切换前 → 执行 Ping 测试 (CellularNetworkValidator)
└─ 如果失败,触发重试逻辑
10. DDS (Default Data Subscription) 切换的优先级
答案:
优先级从高到低:
1. 紧急电话 (最高优先级)
└─ overrideDefaultDataForEmergency()
└─ 临时切换数据卡到紧急卡
2. 语音通话中的卡
├─ isAnyVoiceCallActiveOnDevice() && isUserDataEnabled()
├─ 如果启用"通话中使用数据"
└─ shouldSwitchDataDueToInCall() → 切换到语音卡
3. 机制性切换 (用户/运营商配置)
├─ 用户设置的默认数据卡
├─ Opportunistic 卡 (CBRS)
└─ 自动数据切换推荐
4. 自动双卡切换 (最低优先级)
├─ AutoDataSwitchController 推荐
├─ 网络质量更优的卡
└─ 但不能违反上层优先级
第四部分:Handover(切换)
11. IWLAN ↔ Cellular Handover 的完整过程
答案:
初始状态:DataNetwork 在 Cellular (LTE)
├─ mTransport = TRANSPORT_TYPE_WWAN
└─ mCid (Cellular) = 有效值
触发 Handover:
WiFi 连接 → AccessNetworksManager 通知
↓
DataNetworkController.tryHandoverDataNetwork()
├─ targetTransport = TRANSPORT_TYPE_WLAN
├─ evaluateDataNetworkHandover() → 评估是否允许
└─ dataNetwork.startHandover(TRANSPORT_TYPE_WLAN)
DataNetwork 状态转换:
Connected (WWAN)
↓
(发送 startHandover 到 target transport)
↓
Handover 状态 (中间态)
├─ setupDataCall(WLAN, mDataProfile)
├─ 等待 DataService 回复
│
├─ ✓ 成功:
│ ├─ 切换 mTransport = WLAN
│ ├─ deactivateDataCall(WWAN, cid)
│ ├─ mCid 从 WWAN 切到 WLAN
│ ├─ 更新 mLogTag
│ └─ 返回 Connected (WLAN)
│
└─ ✗ 失败:
├─ cancelHandover(WWAN)
├─ 保持 Connected (WWAN)
├─ 触发重试逻辑
└─ 返回 Connected (WWAN)
关键点:
├─ PDU Session ID:在 source 和 target 之间保持
├─ LinkProperties:可能需要更新
├─ 网络能力:可能需要更新 (QoS 不同)
└─ 超时处理:30 秒无响应则失败
public boolean startHandover(@TransportType int targetTransport,
@Nullable DataHandoverRetryEntry retryEntry) {
if (getCurrentState() == null || isDisconnected() || isDisconnecting()) {
// Fail the request if not in the appropriate state.
if (retryEntry != null) retryEntry.setState(DataRetryEntry.RETRY_STATE_CANCELLED);
return false;
}
// Before we really initiate the handover process on target transport, we need to notify
// source transport that handover is about to start. Handover will be eventually initiated
// in onStartHandover().
sendMessage(obtainMessage(EVENT_NOTIFY_HANDOVER_STARTED, 0, targetTransport, retryEntry));
return true;
}
12. 何时执行 Handover?何时不执行?
答案:
执行 Handover 的情况:
✓ WiFi 连接时:WWAN → WLAN (离线模式)
✓ WiFi 断开时:WLAN → WWAN (恢复蜂窝)
✓ 用户设置允许 IWLAN 切换
✓ 运营商配置允许(KEY_IWLAN_HANDOVER_POLICY_STRING_ARRAY)
✓ 目标传输有服务(NRI.isInService() == true)
不执行 Handover 的情况:
✗ 数据已断开 (isDisconnected() || isDisconnecting())
✗ 数据设置禁用
✗ 通话中且需要 VoPS(Voice over PS)
✗ SIM 不可用
✗ Dual SIM 冲突(某些运营商)
✗ 紧急电话进行中
✗ 运营商禁用该方向切换
✗ 目标传输 OOS
✗ 数据漫游但目标传输漫游被禁
第五部分:权限和 IPC
13. Android Telephony 中的主要权限及其作用
答案:
┌────────────────────────────────────────────────┐
│ READ 权限 (只读) │
├────────────────────────────────────────────────┤
│ READ_PHONE_STATE │
│ └─ 读取电话状态、运营商信息、SIM IMSI │
│ └─ 应用层:TelephonyManager.getSimOperator() │
│ │
│ READ_PRIVILEGED_PHONE_STATE │
│ └─ 只有系统应用可用 │
│ └─ 读取 IMEI、IMSI、运营商 ID、通话记录 │
│ │
│ ACCESS_NETWORK_STATE │
│ └─ 读取网络连接状态 │
│ └─ 由 ConnectivityManager 使用 │
└────────────────────────────────────────────────┘
┌────────────────────────────────────────────────┐
│ WRITE 权限 (修改) │
├────────────────────────────────────────────────┤
│ MODIFY_PHONE_STATE │
│ └─ 启用/禁用飞行模式 │
│ └─ 修改 SIM 卡密码 │
│ └─ 非系统应用无法获得 │
│ │
│ SEND_SMS / RECEIVE_SMS │
│ └─ 发送/接收短信 │
│ │
│ CALL_PHONE │
│ └─ 发起电话 │
└────────────────────────────────────────────────┘
┌────────────────────────────────────────────────┐
│ Carrier Privileges (运营商特权) │
├────────────────────────────────────────────────┤
│ 运营商可使用特殊权限: │
│ ├─ 查询/设置 APN │
│ ├─ 修改网络模式 │
│ ├─ 使用 SMS 接收 OTA 更新 │
│ └─ 通过 SIM 卡证书验证 │
└────────────────────────────────────────────────┘
14. 解释 /dev/binder 和 /dev/hwbinder 的区别
答案:
/dev/binder (传统 Binder)
├─ System Server ↔ Framework 进程
├─ Framework ↔ 应用
├─ 例如:
│ └─ TelephonyManager 查询 Phone 信息
│ → Binder IPC
│ → system_server (Telephony Manager Service)
│ → RIL
│
├─ Stable AIDL:
│ └─ framework_opt_telephony
│ └─ aidl/com/android/internal/telephony/
│ └─ 跨进程通信
/dev/hwbinder (HIDL Binder)
├─ Framework ↔ Vendor HAL 进程
├─ 例如:
│ └─ RIL.getNetworkSelectionMode()
│ → /dev/hwbinder (serialization)
│ → vendor 进程中的 IRadioNetwork
│ → Radio HAL
│
├─ HIDL (Hardware Interface Definition Language)
│ └─ 厂商进程和 Framework 通信
│ ├─ android.hardware.radio@1.0
│ ├─ android.hardware.radio@1.5
│ └─ android.hardware.radio@1.6
关键区别:
┌─────────────────────┬──────────────┬────────────────┐
│ 方面 │ /dev/binder │ /dev/hwbinder │
├─────────────────────┼──────────────┼────────────────┤
│ 通信对象 │ Framework 内 │ Framework-厂商 │
│ 序列化方式 │ Parcel │ HIDL Parcel │
│ API 版本 │ 同一个 │ 版本化的 HAL │
│ 兼容性 │ 松耦合 │ 紧耦合 │
└─────────────────────┴──────────────┴────────────────┘
第六部分:高级问题
15. 如何实现 SMS 延迟返回的解决方案?
答案:
问题场景:
应用调用 SmsManager.sendTextMessage()
↓
Telephony Framework 立即返回 AT+CMGS 命令
↓
Modem 正在查询运营商信息(可能需要几秒)
↓
应用无法知道真正的发送延迟
解决方案:
1. 缓存运营商信息 (ServiceStateTracker)
├─ 定期查询并缓存运营商 MCC/MNC
├─ 避免每次发送 SMS 都查询
└─ 用 SharedPreferences 持久化缓存
2. 异步处理 (Telephony Framework 层)
├─ SmsTracker:追踪发送状态
├─ sendMessage() 延迟处理
└─ 在 Phone Looper 中序列化
3. AT 命令优化 (Modem 层)
├─ 预加载 UDH (User Data Header)
├─ 减少 Modem 查询次数
└─ 合并多个 AT 命令
4. 应用层优化
├─ 使用 PendingIntent 接收发送结果
├─ 异步回调而非阻塞等待
└─ 批量发送多条 SMS
实现参考:
├─ InboundSmsHandler / OutboundSmsHandler
├─ SmsTracker 管理发送队列
├─ ServiceStateTracker 缓存信息
└─ Message 机制异步处理
16. 双卡搜网逻辑(NetworkScan)如何工作?
答案:
NetworkScanRequestTracker (per-SIM)
触发搜网:
应用 → TelephonyManager.requestNetworkScan()
↓ Binder IPC
NetworkScanRequestTracker.startNetworkScan()
↓
RIL.startNetworkScan()
↓ HIDL/Stable AIDL
RadioNetworkProxy.startNetworkScan()
↓
Modem 开始搜网
双卡场景:
┌──────────────────────────────────────┐
│ SIM1 搜网进行中 │
│ NetworkScanRequestTracker-0.start() │
│ ├─ 发送 AT+COPS=? 到 Modem 1 │
│ ├─ 超时:30 秒 │
│ └─ 状态:SCAN_ACTIVE │
└──────────────────────────────────────┘
┌──────────────────────────────────────┐
│ 同时 SIM2 搜网 │
│ NetworkScanRequestTracker-1.start() │
│ ├─ 发送 AT+COPS=? 到 Modem 2 │
│ ├─ 超时:30 秒 │
│ └─ 状态:SCAN_ACTIVE │
└──────────────────────────────────────┘
搜网结果处理:
onNetworkScanResult()
├─ 解析 Modem 返回的网络列表
├─ 排序并过滤
└─ 通知应用或 UI
DSDS 仲裁(争夺 Modem 资源):
├─ 如果 Modem 资源有限
├─ PhoneSwitcher 决定优先级
└─ 优先搜网 DDS (首选数据卡)
17. WakeLock 在 Telephony 中的作用
答案:
WakeLock 类型:
├─ PARTIAL_WAKE_LOCK (最常用)
│ └─ 保持 CPU 运行,允许屏幕关闭
│ └─ 用途:处理 RIL 回调、发送 SMS
│
├─ FULL_WAKE_LOCK (废弃)
│ └─ 保持屏幕亮起
│
└─ SCREEN_DIM_WAKE_LOCK (废弃)
└─ 屏幕变暗但亮起
Telephony 使用场景:
1. RIL 回调处理
├─ RILRequest 获取 WakeLock
├─ 执行回调
└─ 回调完成后释放
├─ 原因:避免 RIL 回调时 CPU 进入睡眠
2. SMS 发送
├─ OutboundSmsHandler 获取 WakeLock
├─ 处理 SMSC 地址查询
├─ 发送 AT 命令到 Modem
└─ 等待确认
├─ 原因:确保 SMS 可靠发送
3. 来电/来短信
├─ InboundSmsHandler 获取 WakeLock
├─ 处理短信存储
├─ 触发通知
└─ 唤醒屏幕
├─ 原因:及时处理来电/短信
代码示例:
// 获取 WakeLock
PowerManager pm = mContext.getSystemService(PowerManager.class);
WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "tag");
wl.acquire(timeout);
try {
// 关键操作
processRilResponse();
} finally {
wl.release(); // 必须在 finally 中释放
}
18. 如何在 Framework 层延迟返回 AT 命令结果?
答案:
需求:
App 调用 getSimOperator()
→ Framework 需要查询 Modem
→ AT+COPS? (可能需要几秒)
→ 返回结果给 App
实现方案:
1. 使用 Message 机制延迟 (推荐)
getNetworkSelectionMode(Message msg) {
RILRequest rr = obtainRequest(..., msg);
mRadioProxy.getNetworkSelectionMode(rr.mSerial);
// msg 会在 Modem 回复后通过 Handler 投递
// Message 会进入 Phone Looper 的 MessageQueue
// 在适当时机由 handleMessage() 处理
}
2. 使用 Callback + Executor 延迟
getNetworkSelectionMode(Executor executor,
Consumer<Integer> callback) {
executor.execute(() -> {
// 在指定 Executor (线程) 上执行
// 可以有延迟
int result = queryModem();
callback.accept(result);
});
}
3. 使用定时器延迟
sendMessageDelayed(msg, delayMillis) {
// 延迟指定时间后投递消息
// 在 Phone Looper 中处理
}
关键点:
├─ 所有操作必须在 Phone Looper 中序列化
├─ 不能阻塞 Binder 线程
├─ 使用 Message / Handler 机制
└─ Callback 通过 Executor 异步返回
第七部分:故障排查和性能
19. 如何调试 Telephony 中的竞态条件?
答案:
常见竞态条件:
1. Phone Looper 线程问题
问题:从错误的线程访问 Phone 对象
调试方法:
├─ checkCorrectThread() 抛出异常
└─ 堆栈追踪定位调用者
解决:使用 Message 投递到 Phone Looper
2. 订阅/状态同步问题
问题:DDS 切换时订阅 ID 不一致
调试方法:
├─ 查看 logcat 中 [ADSC] / [PhoneSwitcher] 日志
├─ 检查 mPreferredDataPhoneId
├─ 对比 mPhoneSubscriptions[]
└─ 验证 mPreferredDataSubId 一致性
tools/emulator:
└─ adb shell "dumpsys telephony.registry"
3. Handover 失败
问题:IWLAN ↔ Cellular 切换时卡住
调试方法:
├─ 检查 DataNetwork 状态机状态
├─ 查看超时日志
├─ 验证 startHandover() 是否调用
└─ 确认 DataService 是否响应
工具:
└─ adb shell "dumpsys connectivity"
4. SMS/通话延迟
问题:处理缓慢导致用户感知延迟
调试方法:
├─ 启用 VDBG 日志
├─ 检查 InboundSmsHandler 队列
├─ 验证 Phone Looper 是否被阻塞
└─ 监控 Binder 线程池
命令:
└─ adb shell "dumpsys telephony" | grep EventLog
20. Telephony 性能优化的最佳实践
答案:
1. 避免主线程 IPC
✗ 错误:
TelephonyManager tm = context.getSystemService(...);
String operatorName = tm.getNetworkOperatorName(); // 阻塞
✓ 正确:
TelephonyCallback callback = new TelephonyCallback() {
void onDisplayInfoChanged(TelephonyDisplayInfo info) {
// 异步回调
}
};
2. 缓存 Telephony 查询结果
✗ 错误:
for (int i = 0; i < 1000; i++) {
String imei = tm.getDeviceId(); // 重复查询
}
✓ 正确:
String cachedImei = tm.getDeviceId();
for (int i = 0; i < 1000; i++) {
// 使用缓存
}
3. 监听变化而非轮询
✗ 错误:
new Thread(() -> {
while (true) {
String operator = tm.getNetworkOperator();
Thread.sleep(1000); // 浪费电量
}
}).start();
✓ 正确:
tm.registerTelephonyCallback(executor, callback);
4. 合理使用 WakeLock
✗ 错误:
wl.acquire(); // 永不释放
✓ 正确:
wl.acquire(timeout); // 或在 finally 中释放
5. 优化搜网过程
├─ 限制搜网频率
├─ 缓存搜网结果
└─ 只在需要时启动搜网
6. 数据连接优化
├─ 使用 Handover 而非重连
├─ 批量处理网络请求
└─ 避免频繁切换 DDS
📖 总结:面试准备清单
理解部分 (必须掌握):
✓ Telephony 整体架构(3 层模型)
✓ Phone Looper 和线程模型
✓ IPC 机制(Binder → HIDL → Stable AIDL)
✓ DataNetwork 状态机(5 个状态)
✓ Handover 流程(IWLAN ↔ Cellular)
深入部分 (加分项):
✓ 自动双卡切换算法(打分系统)
✓ 稳定性检查和重试机制
✓ Telephony 权限体系
✓ WakeLock 管理
✓ 竞态条件调试
代码理解部分:
✓ 阅读核心类源码(GsmCdmaPhone、RIL、DataNetworkController)
✓ 理解回调链(onXxx 方法)
✓ 追踪 Message 流向
实战部分:
✓ 能描述完整的 API 调用链
✓ 能识别潜在的性能问题
✓ 能分析日志定位 bug
这份面经涵盖了 Android Telephony 的核心概念、架构设计、关键算法和实战经验。 💪 希望能帮助你在面试中有所收获!