You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android 9设备CallLog中CACHED_NAME和CACHED_PHOTO_URI返回NULL问题

问题根源分析

在Android 9(API 28)中,谷歌对通话日志的隐私保护机制做了关键更新:系统不再自动将联系人的名称和头像URI缓存到CallLog.Calls.CACHED_NAME和CallLog.Calls.CACHED_PHOTO_URI字段中——哪怕你的应用已经持有READ_CALL_LOG和READ_CONTACTS权限,甚至被设置为默认电话应用。

这是因为Android 9收紧了通话日志与联系人数据的关联逻辑,之前版本里系统会自动同步联系人信息到通话日志的缓存字段,而现在这个自动同步行为被移除,直接导致这两个缓存字段默认返回null,但不涉及联系人关联的字段(比如NUMBER、DURATION)不受影响。

解决方案

既然缓存字段不再可靠,最稳妥的方式是通过获取到的电话号码,直接查询联系人数据库来获取对应的名称和头像URI,具体实现如下:

1. 编写查询联系人信息的工具方法

新增两个工具方法,传入电话号码后从ContactsContract中查询对应数据:

private String getContactNameFromNumber(Context context, String phoneNumber) {
    ContentResolver resolver = context.getContentResolver();
    Uri uri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(phoneNumber));
    
    Cursor cursor = resolver.query(uri, new String[]{ContactsContract.PhoneLookup.DISPLAY_NAME}, null, null, null);
    
    if (cursor != null) {
        try {
            if (cursor.moveToFirst()) {
                return cursor.getString(cursor.getColumnIndex(ContactsContract.PhoneLookup.DISPLAY_NAME));
            }
        } finally {
            cursor.close();
        }
    }
    return null; // 通讯录无匹配号码时返回null
}

private String getContactPhotoUriFromNumber(Context context, String phoneNumber) {
    ContentResolver resolver = context.getContentResolver();
    Uri uri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(phoneNumber));
    
    Cursor cursor = resolver.query(uri, new String[]{ContactsContract.PhoneLookup.PHOTO_URI}, null, null, null);
    
    if (cursor != null) {
        try {
            if (cursor.moveToFirst()) {
                return cursor.getString(cursor.getColumnIndex(ContactsContract.PhoneLookup.PHOTO_URI));
            }
        } finally {
            cursor.close();
        }
    }
    return null;
}

2. 替换原有缓存字段的调用逻辑

把原来直接读取CACHED_NAME和CACHED_PHOTO_URI的代码,替换为调用上面的工具方法:

String number = cursor.getString(cursor.getColumnIndex(CallLog.Calls.NUMBER));
// 替换原name获取逻辑
String name = getContactNameFromNumber(getApplicationContext(), number);
Log.d("tag", "name: " + (name == null ? "null": name));
String date = cursor.getString(cursor.getColumnIndex(CallLog.Calls.DATE));
String duration = cursor.getString(cursor.getColumnIndex(CallLog.Calls.DURATION));
// 替换原photoUri获取逻辑
String photoUri = getContactPhotoUriFromNumber(getApplicationContext(), number);
Log.d("tag", "photoUri: " + (photoUri == null ? "null": photoUri));
// 其他字段保持不变
String subscription_id = cursor.getString(cursor.getColumnIndex(CallLog.Calls.PHONE_ACCOUNT_ID));
String subscription_component_name = cursor.getString(cursor.getColumnIndex(CallLog.Calls.PHONE_ACCOUNT_COMPONENT_NAME));

3. 额外注意事项

  • 确保你的应用已经正确申请并获取了READ_CONTACTS运行时权限(你已确认权限生效,这一步无需额外操作)。
  • 对于通讯录中不存在的号码,工具方法仍会返回null,属于正常行为,你可以根据需求显示号码或默认占位符。

内容的提问来源于stack exchange,提问作者Md Tarik Mahmud

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.12 04:53:32