【问题标题】:Increase speed of fetching contacts with cursor提高使用光标获取联系人的速度
【发布时间】:2020-06-02 08:40:11
【问题描述】:

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

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来加速,但还不够,是否可以摆脱第二个和第三个光标?

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

【问题讨论】:

    标签: android android-contacts android-cursor android-cursorloader


    【解决方案1】:

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

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

    相反,您可以通过一个查询来获取您需要的所有信息,您只需跳过Contacts 表并直接查询包含您需要的所有信息的Data 表。

    我不太明白你在用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 线程。

    【讨论】:

      【解决方案2】:

      如果您的应用被冻结,则意味着您的 SQL 查询在 UI 线程上运行。使用 HandlerThreads、RxJava、Coroutines 等更好地将您的 SQL 查询包装到异步代码中。这可以帮助您不阻塞 UI 线程并因此冻结您的应用程序。

      【讨论】:

        猜你喜欢
        • 2013-12-15
        • 1970-01-01
        • 2013-04-29
        • 2015-12-16
        • 2015-08-04
        • 1970-01-01
        • 1970-01-01
        • 2013-12-27
        • 1970-01-01
        相关资源
        最近更新 更多