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
相关产品推荐
相关产品推荐

