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

Increase speed of fetching contacts with cursor

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

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();
    }

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

我正在使用三个光标,第一个允许我获取 phone

中的所有联系人
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();

第三个循环每phone

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();

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

我尝试使用 CursorLoader 来加快速度,但这还不够,是否可以去掉第二个和第三个光标?

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

如果您的应用冻结,则意味着您的 SQL 查询在 UI 线程上运行。最好使用 HandlerThreads、RxJava、Coroutines e.t.c 将您的 SQL 查询包装到异步代码中。这有助于您不阻塞 UI 线程并因此冻结您的应用程序。

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

因此,在具有 1000 个联系人的 phone 上,您可能需要执行 2001 次查询才能获取所有信息。

相反,您可以通过一个查询来获取所需的所有信息,您只需要跳过 Contacts table 并直接在 Data [=30= 上查询] 其中包含您需要的所有信息。

我不太明白你在用 contact_idname 和那些 MAIL_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 线程。