获取 Android 中的所有联系人数据 - 多个 URI
Posted
技术标签:
【中文标题】获取 Android 中的所有联系人数据 - 多个 URI【英文标题】:Get all contact data in Android - multiple URI 【发布时间】:2014-02-18 05:15:11 【问题描述】:我正在尝试一次性获取所有联系人数据。 我需要 : * 名 * 姓 * 电话号码 * 版本 等等
为此,我正在对整个联系人进行查询,然后,对于每个联系人 ID,我都会获取相关数据。
问题在于它需要很多时间,大约 400 个联系人需要超过 10 秒。 (在三星 i9300 上运行 4.4.2)
我不需要向用户显示任何内容,我只是在后台将联系人与我的服务器同步,所以我没有任何 UI 问题... 只需遍历联系人,看看需要如何更新并将他添加到我的 ArrayList, 然后 POST 到服务器。
public static void getAllContactsFromDevice()
long startTime = System.currentTimeMillis();
Log.d(TAG, "startTime: " + startTime);
// Get all contacts by cursor
ContentResolver cr = getContext().getContentResolver();
Cursor cursor = cr.query(ContactsContract.Contacts.CONTENT_URI, null,
null, null, null);
if (cursor.moveToFirst() && cursor.getCount() > 0)
while (cursor.isAfterLast() == false)
// get contact id on the device DB.
String contactId = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID));
String contactNumber = null;
// Get all phone numbers.
Cursor phones = cr.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = " + contactId, null, null);
// if the user has numbers
// get just the first one for now
if (phones.moveToNext())
contactNumber = phones.getString(phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
// close the cursor to prevent leak
phones.close();
// if we dont have a phone number, continue to next contact.
if (contactNumber == null)
cursor.moveToNext();
continue;
// get first and last name by contact id
String[] projection = new String[]ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME, ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME;
String where = ContactsContract.CommonDataKinds.StructuredName.CONTACT_ID + " = ? AND " + ContactsContract.Data.MIMETYPE + " = ?";
String[] whereParameters = new String[]contactId, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE;
Cursor nameCur = cr.query(ContactsContract.Data.CONTENT_URI, projection, where, whereParameters, null);
String given = null;
String family = null;
try
if (nameCur.getCount() > 0)
nameCur.moveToFirst();
given = nameCur.getString(nameCur.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME));
family = nameCur.getString(nameCur.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME));
nameCur.close();
// if there is no name, continue to next contact.
else
nameCur.close();
cursor.moveToNext();
continue;
catch (Exception e)
e.printStackTrace();
nameCur.close();
if (given == null || given == "null")
given = "";
if (family == null || family == "null")
family = "";
String[] mProjection = new String[]ContactsContract.RawContacts.VERSION;
String[] mWhereParameters = new String[]contactId;
String mWhere = ContactsContract.CommonDataKinds.StructuredName.CONTACT_ID + " = ? ";
Cursor mCursor = cr.query(ContactsContract.RawContacts.CONTENT_URI, mProjection, mWhere, mWhereParameters, null);
int contactVersion = 0;
try
if (mCursor.getCount() > 0)
mCursor.moveToFirst();
contactVersion = mCursor.getInt(mCursor.getColumnIndex(ContactsContract.RawContacts.VERSION));
mCursor.close();
else
mCursor.close();
catch (Exception e)
e.printStackTrace();
mCursor.close();
cursor.moveToNext();
cursor.close();
long endTime = System.currentTimeMillis();
Log.d(TAG, "endTime: " + endTime);
Log.d(TAG, "total time: " + (endTime - startTime));
总时间:18280 毫秒
我查看了HERE developer.android.com,但没有发现任何有用的东西。
this answer 也没有多大帮助。
【问题讨论】:
查看此链接***.com/questions/11991743/… 不,没有帮助,我用它来检查哪些联系人应该与我的服务器联系人同步。我在后台的另一个线程上做... 可以分页吗?先同步一段联系人,然后再滚动同步……这样会减少时间 我没有显示任何内容,只是将所有联系人同步到后台服务器。 【参考方案1】:问题在于,对于每个联系人,您执行的查询很少:电话号码表、结构化姓名表等。您希望将它们全部批处理并对主要联系人列表执行单个查询,对电话号码列表进行单个查询等.
在拥有约 400 个联系人和完整数据的 Genymotion 上,这种方法可提供 30 倍的性能提升(从 2.8 秒到 100 毫秒)。这是我使用的代码:
private static <TLeft, TRight> Function<Pair<TLeft, TRight>, TLeft> getExtractLeftFunction()
return new Function<Pair<TLeft, TRight>, TLeft>()
@Override
public TLeft apply(Pair<TLeft, TRight> input)
return input.first;
;
private static <TLeft, TRight> Map<TLeft, TRight> toMap(Iterable<Pair<TLeft, TRight>> pairs)
Map<TLeft, TRight> result = Maps.newHashMap();
for (Pair<TLeft, TRight> pair : pairs)
result.put(pair.first, pair.second);
return result;
private static class Name
public final String mGivenName;
public final String mFamilyName;
private Name(String givenName, String familyName)
mGivenName = givenName;
mFamilyName = familyName;
public void getAllContactsFromDevice()
long startTime = System.currentTimeMillis();
Log.d(TAG, "startTime: " + startTime);
// Get all contacts by cursor
ContentResolver cr = getContentResolver();
ImmutableList<Long> contactIds = ProviderAction
.query(Contacts.CONTENT_URI)
.projection(Contacts._ID)
.perform(getContentResolver())
.toFluentIterable(SingleRowTransforms.getColumn(Contacts._ID).asLong())
.toImmutableList();
Map<Long, String> phonesMap = toMap(
ProviderAction.query(Phone.CONTENT_URI)
.projection(
Phone.CONTACT_ID,
Phone.NUMBER
)
.whereIn(Phone.CONTACT_ID, contactIds)
.perform(getContentResolver())
.toFluentIterable(getToPairFunction(
SingleRowTransforms.getColumn(Phone.CONTACT_ID).asLong(),
SingleRowTransforms.getColumn(Phone.NUMBER).asString())
)
);
Map<Long, String> contactsWithPhones = Maps.filterValues(phonesMap, Predicates.notNull());
Map<Long, Name> contactNamesMap = toMap(ProviderAction
.query(Data.CONTENT_URI)
.projection(
StructuredName.CONTACT_ID,
StructuredName.FAMILY_NAME,
StructuredName.GIVEN_NAME
)
.whereIn(StructuredName.CONTACT_ID, contactsWithPhones.keySet())
.perform(getContentResolver())
.toFluentIterable(getToPairFunction(
SingleRowTransforms.getColumn(Phone.CONTACT_ID).asLong(),
new Function<Cursor, Name>()
private Function<Cursor, String> mFamilyNameGetter = SingleRowTransforms.getColumn(StructuredName.FAMILY_NAME).asString();
private Function<Cursor, String> mGivenNameGetter = SingleRowTransforms.getColumn(StructuredName.GIVEN_NAME).asString();
@Override
public Name apply(Cursor input)
return new Name(
mGivenNameGetter.apply(input),
mFamilyNameGetter.apply(input)
);
))
);
Map<Long, Integer> versions = toMap(ProviderAction.query(RawContacts.CONTENT_URI)
.projection(
StructuredName.CONTACT_ID,
RawContacts.VERSION
)
.whereIn(StructuredName.CONTACT_ID, contactNamesMap.keySet())
.perform(getContentResolver())
.toFluentIterable(getToPairFunction(
SingleRowTransforms.getColumn(StructuredName.CONTACT_ID).asLong(),
SingleRowTransforms.getColumn(RawContacts.VERSION).asInteger())
)
);
long endTime = System.currentTimeMillis();
Log.d(TAG, "endTime: " + endTime);
Log.d(TAG, "total time: " + (endTime - startTime));
我正在使用android-db-commons(免责声明:我是合著者)来执行所有查询并处理输出。该库不支持将查询结果放入 Map (yet!),这是您要在此处执行的操作,所以上面的代码有点乱,但它仍然胜过同时处理多个游标 IMO。
【讨论】:
以上是关于获取 Android 中的所有联系人数据 - 多个 URI的主要内容,如果未能解决你的问题,请参考以下文章
java - 如何使用json中提供的多个联系人数据在java中的android中添加新的电话联系人