Android按公司和姓名过滤ContactsContract.Data并实现记录去重问题
解决Android ContactsContract.Data查询返回重复联系人记录的问题
问题原因
你当前查询的ContactsContract.Data表是存储联系人所有数据条目的表(比如一个联系人可能有多条公司信息、邮箱、电话等记录)。当你按Organization.MIMETYPE过滤时,同一个联系人如果有多个组织相关的条目,就会返回多条CONTACT_ID相同的记录。
解决方案
有两种可靠的方式实现每个联系人仅返回一条记录:
方案1:改用ContactsContract.Contacts表查询(推荐)
ContactsContract.Contacts表本身就是按联系人维度存储的,每个联系人对应一条记录。通过子查询过滤出符合公司条件的联系人,同时保留姓名过滤逻辑:
private static final Uri URI = ContactsContract.Contacts.CONTENT_URI; @SuppressLint("InlinedApi") private static final String[] PROJECTION = { ContactsContract.Contacts._ID, ContactsContract.Contacts.CONTACT_ID, ContactsContract.Contacts.LOOKUP_KEY, ContactsContract.Contacts.DISPLAY_NAME_PRIMARY }; // 使用EXISTS子查询过滤存在符合条件公司信息的联系人 private static final String SELECTION = ContactsContract.Contacts.DISPLAY_NAME_PRIMARY + " LIKE ?" + " AND EXISTS (" + "SELECT 1 FROM " + ContactsContract.Data.TABLE_NAME + " d " + "WHERE d." + ContactsContract.Data.CONTACT_ID + " = " + ContactsContract.Contacts._ID + " AND d." + ContactsContract.Data.MIMETYPE + " = '" + ContactsContract.CommonDataKinds.Organization.MIMETYPE + "'" + " AND d." + ContactsContract.CommonDataKinds.Organization.COMPANY + " LIKE ?" + ")"; private static final String SORT_ORDER = ContactsContract.Contacts.DISPLAY_NAME_PRIMARY + " ASC"; @Override public Loader<Cursor> onCreateLoader(int loaderId, Bundle args) { String contactsFilter = getFilter(contactsSearch); String companyFilter = getFilter(companySearch); return new CursorLoader( getActivity(), URI, PROJECTION, SELECTION, new String[] { contactsFilter, companyFilter}, SORT_ORDER ); }
方案2:在Data表查询中添加去重逻辑
如果坚持使用ContactsContract.Data表,需要去掉会导致行唯一的Data._ID字段,并添加去重参数:
- 修改投影字段(移除
Data._ID,因为每个数据项的_ID唯一,会导致去重失效):
@SuppressLint("InlinedApi") private static final String[] PROJECTION = { ContactsContract.Data.CONTACT_ID, ContactsContract.Data.LOOKUP_KEY, ContactsContract.Data.DISPLAY_NAME_PRIMARY };
- 构建带去重参数的URI:
@Override public Loader<Cursor> onCreateLoader(int loaderId, Bundle args) { String contactsFilter = getFilter(contactsSearch); String companyFilter = getFilter(companySearch); // 添加distinct参数实现去重 Uri distinctUri = ContactsContract.Data.CONTENT_URI.buildUpon() .appendQueryParameter("distinct", "true") .build(); return new CursorLoader( getActivity(), distinctUri, PROJECTION, SELECTION, new String[] { contactsFilter, companyFilter}, SORT_ORDER ); }
或者也可以使用GROUP BY CONTACT_ID的方式,需要调整投影为聚合字段(比如取最小的_ID):
@SuppressLint("InlinedApi") private static final String[] PROJECTION = { "MIN(" + ContactsContract.Data._ID + ") AS _id", // 聚合取该联系人的第一条数据ID ContactsContract.Data.CONTACT_ID, ContactsContract.Data.LOOKUP_KEY, ContactsContract.Data.DISPLAY_NAME_PRIMARY }; private static final String SELECTION = ContactsContract.Data.DISPLAY_NAME_PRIMARY + " LIKE ?" + " AND " + ContactsContract.Data.MIMETYPE + " = " + ContactsContract.CommonDataKinds.Organization.MIMETYPE + " AND " + ContactsContract.CommonDataKinds.Organization.COMPANY + " LIKE ?" + " GROUP BY " + ContactsContract.Data.CONTACT_ID; // 按CONTACT_ID分组
效果验证
修改后查询返回的Cursor中,每个CONTACT_ID和LOOKUP_KEY只会对应一条记录,符合你的需求。
内容的提问来源于stack exchange,提问作者CompEng88
相关产品推荐
相关产品推荐

