
本文介绍如何利用 contactscontract.contacts.contact_last_updated_timestamp 字段精准筛选出最近被修改(如姓名、电话等字段变更)的联系人,避免全量遍历,提升性能与响应效率。
本文介绍如何利用 contactscontract.contacts.contact_last_updated_timestamp 字段精准筛选出最近被修改(如姓名、电话等字段变更)的联系人,避免全量遍历,提升性能与响应效率。
在 Android 联系人开发中,仅监听变更事件(如通过 ContentObserver)并不足以满足业务需求——关键在于识别哪些联系人真正发生了更新。原代码中直接查询 Phone.CONTENT_URI 会返回所有带号码的联系人记录,无法区分“新增”“修改”或“未变”,且存在重复(同一联系人可能有多个号码)。正确做法是:以联系人实体(ContactsContract.Contacts)为单位,按最后更新时间戳筛选。
✅ 正确查询已更新联系人的核心逻辑
ContactsContract.Contacts.CONTACT_LAST_UPDATED_TIMESTAMP 是系统维护的时间戳(毫秒级 Unix 时间),每次联系人任意字段(包括姓名、号码、邮箱、头像等)被修改时自动更新。因此,只需按该字段排序并限定范围即可高效获取增量变更:
public void contactChanged() {
ContentResolver resolver = getContentResolver();
String[] projection = {
ContactsContract.Contacts._ID,
ContactsContract.Contacts.DISPLAY_NAME,
ContactsContract.Contacts.CONTACT_LAST_UPDATED_TIMESTAMP
};
// 按更新时间倒序,最新修改的排在最前
String sortOrder = ContactsContract.Contacts.CONTACT_LAST_UPDATED_TIMESTAMP + " DESC";
try (Cursor cursor = resolver.query(
ContactsContract.Contacks.CONTENT_URI,
projection,
null, null, sortOrder)) {
if (cursor == null || cursor.getCount() == 0) return;
JSONArray updatedContacts = new JSONArray();
while (cursor.moveToNext()) {
String id = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.Contacts._ID));
String name = cursor.getString(cursor.getColumnIndexOrThrow(ContactsContract.Contacts.DISPLAY_NAME));
long lastUpdated = cursor.getLong(cursor.getColumnIndexOrThrow(ContactsContract.Contacts.CONTACT_LAST_UPDATED_TIMESTAMP));
// ⚠️ 关键建议:添加时间过滤(例如仅过去5分钟内更新)
// long cutoff = System.currentTimeMillis() - 5 * 60 * 1000;
// if (lastUpdated <h3>? 注意事项与最佳实践</h3>
-
权限必需:确保已在 AndroidManifest.xml 中声明
android:name="android.permission.READ_CONTACTS" />,并在运行时动态申请(Android 6.0+)。 - 避免全量扫描:CONTACT_LAST_UPDATED_TIMESTAMP 是唯一可靠的变更标识,切勿依赖 onChange(boolean selfChange) 的 selfChange 参数——它仅表示变更是否由本应用触发,无法反映实际数据变化。
-
性能优化建议:
- 在 query() 后添加 WHERE 条件(如 CONTACT_LAST_UPDATED_TIMESTAMP > ?)限制时间范围,大幅减少结果集;
- 使用 try-with-resources 确保 Cursor 及时关闭(示例已体现);
- 若需获取号码/邮箱等详细字段,应以 contactId 为条件二次查询对应 Data 表,而非直接查 Phone.CONTENT_URI(该 URI 无 CONTACT_LAST_UPDATED_TIMESTAMP 字段)。
- 生命周期管理:务必在 Activity.onDestroy() 或 Fragment.onDestroyView() 中调用 getContentResolver().unregisterContentObserver(observer),防止内存泄漏。
通过上述方式,你将获得真正意义上“被编辑过”的联系人列表,为同步、备份或实时 UI 更新提供精准、轻量的数据源。











