CallLog Provider:通话记录的那些事儿
通话记录这个功能,说实话,每个手机都有。但你真的了解它背后的机制吗?
我个人习惯把 CallLog Provider 看作是 Android 系统里最「接地气」的 Content Provider 之一。为什么这么说?因为它直接关系到用户的日常通信行为——谁打过电话、打了多久、是接听还是未接,这些数据都藏在 CallLog 表里。
CallLog Provider 是什么?
说白了,它就是系统提供的一个公开接口,用来读写通话记录。你手机里那个「通话记录」列表,底层就是靠它来支撑的。
我记得有一次做企业通讯录 App,需要把通话记录同步到后台做分析。当时第一反应就是:直接查 CallLog 表不就行了?嗯,确实可以,但要注意权限问题。
content://call_log/calls 这个 URI 下。
查询通话日志
查询通话记录,其实就是一个标准的 ContentResolver 查询操作。我直接上代码吧,这样更直观。
// 查询通话记录
ContentResolver resolver = getContentResolver();
Uri uri = CallLog.Calls.CONTENT_URI;
// 我只关心最近 50 条记录
String selection = null;
String[] selectionArgs = null;
String sortOrder = CallLog.Calls.DATE + " DESC LIMIT 50";
Cursor cursor = resolver.query(
uri,
null, // 查询所有列
selection,
selectionArgs,
sortOrder
);
if (cursor != null) {
while (cursor.moveToNext()) {
String number = cursor.getString(
cursor.getColumnIndex(CallLog.Calls.NUMBER));
long duration = cursor.getLong(
cursor.getColumnIndex(CallLog.Calls.DURATION));
int type = cursor.getInt(
cursor.getColumnIndex(CallLog.Calls.TYPE));
long date = cursor.getLong(
cursor.getColumnIndex(CallLog.Calls.DATE));
// 处理数据...
}
cursor.close();
}
这里有个坑,我曾经踩过:CallLog.Calls.TYPE 返回的是 int 值,对应关系如下:
| 类型常量 | 值 | 含义 |
|---|---|---|
| INCOMING_TYPE | 1 | 来电 |
| OUTGOING_TYPE | 2 | 去电 |
| MISSED_TYPE | 3 | 未接 |
| VOICEMAIL_TYPE | 4 | 语音邮件 |
| REJECTED_TYPE | 5 | 已拒绝 |
| BLOCKED_TYPE | 6 | 已拦截 |
CallLog.Calls.TYPE + " = ?",参数传 "3"。
插入通话记录
插入通话记录,这个操作稍微敏感一些。为什么?因为系统不希望任何 App 随意篡改通话记录。你想想看,如果某个恶意 App 往你通话记录里插一堆虚假数据,那不乱套了?
不过,在 Android 10(API 29)之前,确实可以通过 ContentResolver 直接插入。我当年做过一个通话录音 App,就用了这个方式把录音标记和通话记录关联起来。
// 插入一条通话记录(Android 10 之前可用)
ContentValues values = new ContentValues();
values.put(CallLog.Calls.NUMBER, "13800138000");
values.put(CallLog.Calls.TYPE, CallLog.Calls.OUTGOING_TYPE);
values.put(CallLog.Calls.DURATION, 120); // 单位:秒
values.put(CallLog.Calls.DATE, System.currentTimeMillis());
values.put(CallLog.Calls.NEW, 0); // 0 表示已查看
Uri insertedUri = getContentResolver().insert(
CallLog.Calls.CONTENT_URI, values);
WRITE_CALL_LOG 权限,并且系统会限制非默认拨号器 App 的写入行为。说白了,普通 App 已经不能随便插了。
权限问题
说到权限,这是 CallLog Provider 里最容易出问题的地方。我见过太多开发者因为权限没处理好导致崩溃。
- 读取权限:
READ_CALL_LOG,需要在 AndroidManifest 中声明,并且是危险权限,需要运行时申请。 - 写入权限:
WRITE_CALL_LOG,同样需要声明和运行时申请。 - 特殊说明: 如果你的 App 是默认拨号器,可以免去部分限制。但默认拨号器的申请流程比较复杂,不是随便就能拿到的。
我曾经遇到一个 case:用户反馈 App 在 Android 11 上查询通话记录时直接闪退。排查后发现,是运行时权限申请成功后,没有重新查询,导致 cursor 为 null。嗯,这个坑大家要注意——权限申请成功后,最好重新走一遍查询流程。
知识体系结构图
下面这张图,我把 CallLog Provider 的核心知识点串起来了。你看一眼就能明白整体脉络。
避坑指南
最后,我把自己这些年踩过的坑总结一下,希望能帮你少走弯路。
- Cursor 一定要关闭: 我曾经因为忘记 close cursor,导致内存泄漏,被测试小姐姐追着骂了一周。
- 权限申请时机: 不要在 onCreate 里直接申请权限,等用户真正需要查看通话记录时再弹窗,体验会好很多。
- 数据格式:
CallLog.Calls.DATE返回的是毫秒级时间戳,直接拿来用就行。但CallLog.Calls.DURATION是秒数,显示时要转成时分秒格式。 - 空指针处理: 查询结果可能为空,或者某些列不存在(不同厂商 ROM 可能有差异),记得加判空。
好了,关于 CallLog Provider 的核心内容就这些。说白了,它就是一套标准的 Content Provider 操作,只是多了权限和版本限制。你只要把查询、插入、权限这三块搞明白,基本就能应对大部分场景了。