提高用光标提取联系人的速度



在我的应用程序中,我用游标获取所有联系人。

private static final String[] PROJECTION = new String[]{
PHONE_CONTACT_ID,
DISPLAY_NAME,
TIMESTAMP,
HAS_PHONE_NUMBER};
CursorLoader cursorLoader = new CursorLoader(ResUtils.getInstance().getContext(),
ContactsContract.Contacts.CONTENT_URI, PROJECTION, null, null,
"UPPER(" + ContactsContract.Contacts.DISPLAY_NAME + ")ASC");
Cursor cursor = cursorLoader.loadInBackground();
// Loop for every contact in the phone
if (cursor != null && cursor.getCount() > 0) {
while (cursor.moveToNext()) {
ArrayList<String> phoneNumbers = new ArrayList<>();
String contact_id = cursor.getString(cursor.getColumnIndex(PHONE_CONTACT_ID));
String   name = cursor.getString(cursor.getColumnIndex(DISPLAY_NAME));
String  timeStamp = cursor.getString(cursor.getColumnIndex(TIMESTAMP));
cursorLoader = new CursorLoader( ResUtils.getInstance().getContext(), ContactsContract.CommonDataKinds.Email.CONTENT_URI,
new String[]{EMAIL},ContactsContract.CommonDataKinds.Email.CONTACT_ID + " = ?", new String[]{contact_id}, null);
Cursor emailCur = cursorLoader.loadInBackground();
while (emailCur.moveToNext()) {
String email = emailCur.getString(emailCur.getColumnIndex(EMAIL));
if (TmlyUtils.isValidEmail(email)) {
phoneNumbers.add(ContactUtils.MAIL_TAG + email);
}
}
emailCur.close();
if (Integer.parseInt(cursor.getString(cursor.getColumnIndex(HAS_PHONE_NUMBER))) > 0) {
cursorLoader = new CursorLoader( ResUtils.getInstance().getContext(),   ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
new String[]{NUMBER},ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ?", new String[]{contact_id}, null);
Cursor phones = cursorLoader.loadInBackground();
while (phones.moveToNext()) {
String phoneNumber = phones.getString(phones.getColumnIndex(NUMBER));
phoneNumber = isValidMobileNumber(phoneNumber);
if (!phoneNumber.isEmpty() && !phoneNumbers.contains(ContactUtils.UPN_TAG + phoneNumber)) {
phoneNumbers.add(ContactUtils.UPN_TAG + phoneNumber);
}
}
phones.close();
}
}
cursor.close();
}

代码运行良好,但当你有数千个联系人时,应用程序会在几秒钟内冻结。

我使用三个光标,第一个允许我在手机中获取所有联系人

CursorLoader cursorLoader = new CursorLoader(ResUtils.getInstance().getContext(),
ContactsContract.Contacts.CONTENT_URI, PROJECTION, null, null,
"UPPER(" + ContactsContract.Contacts.DISPLAY_NAME + ")ASC");
Cursor cursor = cursorLoader.loadInBackground();

第二个循环用于每个电子邮件地址

cursorLoader = new CursorLoader( ResUtils.getInstance().getContext(), ContactsContract.CommonDataKinds.Email.CONTENT_URI,
new String[]{EMAIL},ContactsContract.CommonDataKinds.Email.CONTACT_ID + " = ?", new String[]{contact_id}, null);
Cursor emailCur = cursorLoader.loadInBackground();

第三个一环为每部手机

cursorLoader = new CursorLoader( ResUtils.getInstance().getContext(),   ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
new String[]{NUMBER},ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ?", new String[]{contact_id}, null);
Cursor phones = cursorLoader.loadInBackground();

我需要使用第二个和第三个如果我不使用它,我无法获得每个联系人的所有电话和电子邮件,因为一个联系人可以有几个电话和电子邮件地址

我试图使用CursorLoader来加快速度,但这还不够,有可能去掉第二个和第三个光标吗?

编辑:我忘了说所有的查询都已经在改装调用中了

在使用Android ContactsContract API时,这是一个非常常见的问题,您基本上要进行一个大查询来获取所有联系人,然后进行大量小查询来获取电话和电子邮件。

因此,在一部有1000个联系人的手机上,你可能需要进行2001次查询才能获得所有信息。

相反,您可以使用一个查询来获取所需的所有信息,只需跳过Contacts表,直接在包含所有所需信息的Data表上进行查询。

我不太明白你在用contact_idnameMAIL_TAG/UPN_TAG做什么,所以我只会将你需要的信息打印到日志中,你需要将其分类到对你的应用程序有意义的java对象中。

String[] projection = {Data.CONTACT_ID, Data.DISPLAY_NAME, Data.MIMETYPE, Data.DATA1};
String selection = Data.MIMETYPE + " IN ('" + Phone.CONTENT_ITEM_TYPE + "', '" + Email.CONTENT_ITEM_TYPE + "')";
Cursor cur = cr.query(Data.CONTENT_URI, projection, selection, null, null);
while (cur.moveToNext()) {
long id = cur.getLong(0); // contact-id
String name = cur.getString(1); // contact name
String mime = cur.getString(2); // type: email / phone
String data = cur.getString(3); // the actual info, e.g. +1-212-555-1234
switch (mime) {
case Phone.CONTENT_ITEM_TYPE: 
Log.i("Contacts", "got a phone: " + id + ", " + name + ", " + data);
break;
case Email.CONTENT_ITEM_TYPE: 
Log.i("Contacts", "got an email: " + id + ", " + name + ", " + data);
break;
}
}
cur.close();

此外,正如@IliaKuzmin所说,你应该在后台线程上运行它,而不是在UI线程上。

如果您的应用程序被冻结,则意味着您的SQL查询在UI线程上运行。使用HandlerThreads、RxJava、Coroutines e.t.c.将SQL查询更好地包装成异步代码。这有助于您避免阻塞UI线程,从而冻结应用程序。

最新更新