使用CursorLoader获取电子邮件会导致邮件重复

我正在尝试使用联系人的电子邮件ID。 为此,我正在使用游标加载器。 有一个问题,我也得到重复的电子邮件ID。 如何删除电子邮件的重复。 我应该使用原始查询“SELECT DISTINCT”,而不是使用CursorLoader还是有一些其他的解决scheme?

@Override public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) { String[] projection = new String[] { ContactsContract.Contacts._ID, ContactsContract.Contacts.DISPLAY_NAME, ContactsContract.CommonDataKinds.Email.DATA}; String sortOrder = ContactsContract.Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC"; String selection = ContactsContract.Contacts.IN_VISIBLE_GROUP +"='1' AND " + Email.DATA +" IS NOT NULL AND " + Email.DATA +" != \"\" " ; //showing only visible contacts String[] selectionArgs = null; return new CursorLoader(this, ContactsContract.CommonDataKinds.Email.CONTENT_URI, projection, selection, selectionArgs, sortOrder); } 

Solutions Collecting From Web of "使用CursorLoader获取电子邮件会导致邮件重复"

我最近碰到这个问题。 看来CursorLoader没有“DISTINCT”的实现。 我的解决方法将几行添加到onLoadFinish方法,并扩展BaseAdapter以接受List参数:

 @Override public Loader<Cursor> onCreateLoader(int id, Bundle args) { String projection[] = { CommonDataKinds.Phone._ID, CommonDataKinds.Phone.DISPLAY_NAME, }; String select = "((" + CommonDataKinds.Phone.DISPLAY_NAME + " NOTNULL) and " + CommonDataKinds.Phone.HAS_PHONE_NUMBER + " > 0)"; String sort = CommonDataKinds.Phone.DISPLAY_NAME + " ASC"; CursorLoader loader = new CursorLoader( mContext, CommonDataKinds.Phone.CONTENT_URI, projection, select, null, sort ); return loader; } @Override public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) { List<String> displayNames = new ArrayList<String>(); cursor.moveToFirst(); while(!cursor.isAfterLast()){ String name = cursor.getString(cursor.getColumnIndex(CommonDataKinds.Phone.DISPLAY_NAME)); if(!displayNames.contains(name)) displayNames.add(name); cursor.moveToNext(); } mAdapter.swapCursor(displayNames); } 

这是我的BaseAdapter类:

 public class AdapterAddContacts extends BaseAdapter{ private List<String> mData = new ArrayList<String>(); private Context mContext; public AdapterAddContacts(Context context,List<String> displayNames){ mData = displayNames; mContext = context; } @Override public int getCount() { if(mData != null) return mData.size(); else return 0; } @Override public Object getItem(int pos) { return mData.get(pos); } @Override public long getItemId(int id) { return id; } @Override public View getView(int pos, View convertView, ViewGroup parent) { LayoutInflater inflater = LayoutInflater.from(mContext); View view = inflater.inflate(R.layout.entry_add_contacts,parent,false); String data = mData.get(pos); TextView textName = (TextView)view.findViewById(R.id.my_contacts_add_display_name); textName.setText(data); textName.setTag(data); return view; } public void swapCursor(List<String> displayNames){ mData = displayNames; this.notifyDataSetChanged(); } 

你应该可以根据你的需要修改它。

受@mars启发,我有一个不需要修改适配器的解决scheme。 这个想法是删除游标的副本; 因为没有办法做到这一点,我们创build一个新的游标,而不是重复的。

所有的代码在onLoadFinished

 @Override public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) { MatrixCursor newCursor = new MatrixCursor(PROJECTION); // Same projection used in loader if (cursor.moveToFirst()) { String lastName = ""; do { if (cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)).compareToIgnoreCase(lastName) != 0) { newCursor.addRow(new Object[]{cursor.getString(0), cursor.getString(1), cursor.getString(2) ...}); // match the original cursor fields lastName =cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)); } } while (cursor.moveToNext()); } mContactsAdapter.swapCursor(newCursor); } 

我在我的项目中使用了一个小小的黑客攻击,就像这样:

 @Override public Loader<Cursor> onCreateLoader(int id, Bundle args) { return new CursorLoader( this, MediaStore.Images.Media.EXTERNAL_CONTENT_URI, new String[] { "DISTINCT "+ MediaStore.Images.Media.BUCKET_ID, MediaStore.Images.Media.BUCKET_DISPLAY_NAME}, null, null, null); } 

此代码仅从库中返回包名称和ID。 所以,我会重写你的代码:

 @Override public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) { String[] projection = new String[] { "DISTINCT " + ContactsContract.Contacts._ID, ContactsContract.Contacts.DISPLAY_NAME, ContactsContract.CommonDataKinds.Email.DATA}; String sortOrder = ContactsContract.Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC"; String selection = ContactsContract.Contacts.IN_VISIBLE_GROUP +"='1' AND " + Email.DATA +" IS NOT NULL AND " + Email.DATA +" != \"\" " ; //showing only visible contacts String[] selectionArgs = null; return new CursorLoader(this, ContactsContract.CommonDataKinds.Email.CONTENT_URI, projection, selection, selectionArgs, sortOrder); } 

你可以把setDistinct放到你的内容提供者中。

  @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { ... final SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); qb.setDistinct(true); 

如果你担心性能,不想在onLoadFinished()中再次使用游标,那么有一个小小的黑客

我结合了SO的两个解决scheme。

  1. 在android sqlite中select不同的值

  2. 带有rawQuery的CursorLoader

这里是我的工作解决scheme:

  @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { String tableName; /* * Choose the table to query and a sort order based on the code returned * for the incoming URI. */ switch (uriMatcher.match(uri)) { case NOTIFICATION: tableName = NOTIFICATIONS_TABLE_NAME; break; case NOTIFICATION_TIMESTAMP: Cursor cursor = db.query(true, NOTIFICATIONS_TABLE_NAME, projection, selection, selectionArgs, TIMESTAMP, null, sortOrder, null); cursor.setNotificationUri(getContext().getContentResolver(), uri); return cursor; case DOWNLOAD: tableName = DOWNLOADS_TABLE; break; default: throw new IllegalArgumentException("Unknown URI " + uri); } if (selection != null) { selection = selection + "=?"; } Cursor cursor = db.query(tableName, projection, selection, selectionArgs, null, null, sortOrder); // Tell the cursor what uri to watch, so it knows when its source data // changes cursor.setNotificationUri(getContext().getContentResolver(), uri); return cursor; } 

如果你在这种情况下看到表名是相同的是前两种情况,但我创build了一个虚拟的Uri来实现这一点。 可能不是一个很好的方法,但完美的作品。

我发现一个解决scheme在select数组中使用DISTINCT关键字。

 String[] projection = new String[] { ContactsContract.Contacts._ID, ContactsContract.Contacts.DISPLAY_NAME, "DISTINCT" + ContactsContract.CommonDataKinds.Email.DATA};