使用 CursorLoader 获取电子邮件会导致电子邮件重复
Posted
技术标签:
【中文标题】使用 CursorLoader 获取电子邮件会导致电子邮件重复【英文标题】:Using CursorLoader to get emails causes duplication of emails 【发布时间】:2013-01-18 19:20:19 【问题描述】:我正在尝试获取使用联系人的电子邮件 ID。为此,我正在使用光标加载器。还有一个问题,我也收到重复的电子邮件 ID。如何删除电子邮件重复。我应该使用原始查询“SELECT DISTINCT”而不是使用 CursorLoader 还是有其他解决方案?
@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);
【问题讨论】:
【参考方案1】:我最近遇到了这个问题。 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();
您应该能够根据自己的需要专门修改它。
【讨论】:
感谢火星,我曾经使用过这种方法。正在寻找其他一些我不必使用内存的解决方案。 我做了类似的事情,但使用 Cursor 而不是 List。【参考方案2】:受@mars 启发,我有一个不需要修改适配器的解决方案。这个想法是删除光标的重复项;由于没有办法做到这一点,我们创建一个没有重复的新光标。
所有代码都在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);
【讨论】:
如果项目不包含 DISPLAY_NAME compareToIgnore 将有错误,因此最好在 cursorLoader 的 WHERE 子句中指定您不接受任何为空的 Name 值。【参考方案3】:我在我的项目中使用了一个小技巧 - SQL 注入,就像这样:
@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);
【讨论】:
【参考方案4】:您可以将setDistinct 放在您的内容提供商中。
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)
...
final SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
qb.setDistinct(true);
【讨论】:
问题是关于 CursorLoader,而不是 SQL 查询【参考方案5】:如果您担心性能并且不想在 onLoadFinished() 中再次使用光标,那么有一个小技巧
我结合了 SO 的以下两种解决方案。
select distinct value in android sqlite
CursorLoader with rawQuery
这是我的工作解决方案:
@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;
如果您在这种情况下看到表名与前两种情况相同,但我创建了一个虚拟 Uri 来实现这一点。可能不是一个很好的方法,但效果很好。
【讨论】:
【参考方案6】:我找到了解决方案
在选择数组中使用DISTINCT
关键字。
String[] projection = new String[] ContactsContract.Contacts._ID, ContactsContract.Contacts.DISPLAY_NAME, "DISTINCT" + ContactsContract.CommonDataKinds.Email.DATA;
【讨论】:
以上是关于使用 CursorLoader 获取电子邮件会导致电子邮件重复的主要内容,如果未能解决你的问题,请参考以下文章
Xamarin Android,使用带有选择和选择参数的 CursorLoader 获取联系人手机号码
Android:CursorLoader 在非最顶层 Fragment 上崩溃