22

在我的项目中,获取联系人需要很长时间才能加载。

  • 有什么方法可以减少接触时间
  • 假设我的手机中有 1000 个联系人。
  • 现在加载所有联系人需要超过 2 分钟

如何减少加载联系人的时间?有什么想法吗?

我在编写初始方法时参考了以下链接。

http://www.coderzheaven.com/2011/06/13/get-all-details-from-contacts-in-android/

4

6 回答 6

31

更好的解决方案在这里......

private static final String[] PROJECTION = new String[] {
        ContactsContract.CommonDataKinds.Phone.CONTACT_ID,
        ContactsContract.Contacts.DISPLAY_NAME,
        ContactsContract.CommonDataKinds.Phone.NUMBER
    };
.
.
.

ContentResolver cr = getContentResolver();
        Cursor cursor = cr.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, PROJECTION, null, null, null);
        if (cursor != null) {
            try {
                final int nameIndex = cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME);
                final int numberIndex = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);

                String name, number;
                while (cursor.moveToNext()) {
                    name = cursor.getString(nameIndex);
                    number = cursor.getString(numberIndex);
                }
            } finally {
                cursor.close();
            }
        }

干杯...:)

于 2015-02-24T07:13:23.350 回答
10

总时间取决于您尝试从“联系人”表访问的字段。访问更少的字段意味着更少的循环,更少的处理,因此更快的结果。

此外,为了加快您的联系人获取操作,您可以使用 ContentProvideClient 而不是每次都在 ContentResolver 上调用查询。这将使您查询特定的表,而不是先查询所需的 ContentProvider,然后再查询表。

创建 ContentProviderClient 的实例

ContentResolver cResolver=context.getContextResolver();
ContentProviderClient mCProviderClient = cResolver.acquireContentProviderClient(ContactsContract.Contacts.CONTENT_URI);

然后重用此 mCProviderClient 以在您的通话中获取联系人(来自任何 ContentProvider 的数据)数据。例如在以下方法中,我只访问一个字段。

private ArrayList<String> fetchContactsCProviderClient()
    {
        ArrayList<String> mContactList = null;
        try
        {
            Cursor mCursor = mCProviderClient.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);
            if (mCursor != null && mCursor.getCount() > 0)
            {
                mContactList = new ArrayList<String>();
                mCursor.moveToFirst();
                while (!mCursor.isLast())
                {
                    String displayName = mCursor.getString(mCursor.getColumnIndexOrThrow(ContactsContract.Contacts.DISPLAY_NAME));
                    mContactList.add(displayName);
                    mCursor.moveToNext();
                }
                if (mCursor.isLast())
                {
                    String displayName = mCursor.getString(mCursor.getColumnIndexOrThrow(ContactsContract.Contacts.DISPLAY_NAME));
                    mContactList.add(displayName);
                }
            }

            mCursor.close();
        }
        catch (RemoteException e)
        {
            e.printStackTrace();
            mContactList = null;
        }
        catch (Exception e)
        {
            e.printStackTrace();
            mContactList = null;
        }

        return mContactList;
    }
于 2012-07-13T07:56:59.937 回答
8

像其他应用程序一样更快地加载联系人。

我已经用多个联系人测试了这段代码,它像其他应用程序一样500 ms在半秒或更短的时间内工作得很好,而且速度更快,我能够加载1000+联系人。

总时间取决于您尝试从“联系人”表访问的字段。

根据您的要求管理您的查询不要访问不需要的字段。访问更少的字段意味着更少的循环,更少的处理,因此更快的结果。

在联系人中访问正确的表也有助于减少联系人加载时间。

查询优化加载联系人更快速使用projection

 String[] projection = {
            ContactsContract.Data.MIMETYPE,
            ContactsContract.Data.CONTACT_ID,
            ContactsContract.Contacts.DISPLAY_NAME,
            ContactsContract.Contacts.PHOTO_URI,
            ContactsContract.Contacts.STARRED,
            ContactsContract.RawContacts.ACCOUNT_TYPE,
            ContactsContract.CommonDataKinds.Contactables.DATA,
            ContactsContract.CommonDataKinds.Contactables.TYPE
    };

选择和选择参数

String selection = ContactsContract.Data.MIMETYPE + " in (?, ?)" + " AND " /*+ ContactsContract.Contacts.IN_VISIBLE_GROUP + " = '" + 1 + "' AND "*/ +
            ContactsContract.Data.HAS_PHONE_NUMBER + " = '" + 1 + "'";

    String[] selectionArgs = {
            ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE,
            ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE,
    };

要按字母顺序订购联系人,请使用以下代码

 try {
        Collections.sort(listview_address, new Comparator<ContactBook>() {
            @Override
            public int compare(ContactBook lhs, ContactBook rhs) {
                return lhs.name.toUpperCase().compareTo(rhs.name.toUpperCase());
            }
        });
    } catch (Exception e) {
        e.printStackTrace();
    }

以下是完整的源代码

 public void initeContacts() {
    List<ContactBook> listview_address = new LinkedList<ContactBook>();
    SparseArray<ContactBook> addressbook_array = null;
    {
        addressbook_array = new SparseArray<ContactBook>();

        long start = System.currentTimeMillis();

        String[] projection = {
                ContactsContract.Data.MIMETYPE,
                ContactsContract.Data.CONTACT_ID,
                ContactsContract.Contacts.DISPLAY_NAME,
                ContactsContract.Contacts.PHOTO_URI,
                ContactsContract.Contacts.STARRED,
                ContactsContract.RawContacts.ACCOUNT_TYPE,
                ContactsContract.CommonDataKinds.Contactables.DATA,
                ContactsContract.CommonDataKinds.Contactables.TYPE
        };

        String selection = ContactsContract.Data.MIMETYPE + " in (?, ?)" + " AND " /*+ ContactsContract.Contacts.IN_VISIBLE_GROUP + " = '" + 1 + "' AND "*/ +
                ContactsContract.Data.HAS_PHONE_NUMBER + " = '" + 1 + "'";

        String[] selectionArgs = {
                ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE,
                ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE,
        };

        String sortOrder = ContactsContract.Contacts.SORT_KEY_ALTERNATIVE;

        Uri uri = null;
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR2) {
            uri = ContactsContract.CommonDataKinds.Contactables.CONTENT_URI;
        } else {
            uri = ContactsContract.Data.CONTENT_URI;

        }
        // we could also use Uri uri = ContactsContract.Data.CONTENT_URI;
        // we could also use Uri uri = ContactsContract.Contact.CONTENT_URI;

        Cursor cursor = getActivity().getContentResolver().query(uri, projection, selection, selectionArgs, sortOrder);


        final int mimeTypeIdx = cursor.getColumnIndex(ContactsContract.Data.MIMETYPE);
        final int idIdx = cursor.getColumnIndex(ContactsContract.Data.CONTACT_ID);
        final int nameIdx = cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME);
        final int dataIdx = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Contactables.DATA);
        final int photo = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Contactables.PHOTO_URI);
        final int typeIdx = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Contactables.TYPE);
        final int account_type = cursor.getColumnIndex(ContactsContract.RawContacts.ACCOUNT_TYPE);
        while (cursor.moveToNext()) {
            int contact_id = cursor.getInt(idIdx);
            String photo_uri = cursor.getString(photo);
            String contact_name = cursor.getString(nameIdx);
            String contact_acc_type = cursor.getString(account_type);
            int contact_type = cursor.getInt(typeIdx);
            String contact_data = cursor.getString(dataIdx);
            ContactBook contactBook = addressbook_array.get(contact_id);

           /* if (contactBook == null) {
                //list  contact add to avoid duplication
                //load All contacts fro device
                //to add contacts number with name add one extra veriable in ContactBook as number and pass contact_data this give number to you (contact_data is PHONE NUMBER)
                contactBook = new ContactBook(contact_id, contact_name, getResources(), photo_uri, contact_acc_type, "phone number");
                addressbook_array.put(contact_id, contactBook);
                listview_address.add(contactBook);

            }*/

            String Contact_mimeType = cursor.getString(mimeTypeIdx);
            //here am checking Contact_mimeType to get mobile number asociated with perticular contact and email adderess asociated
            if (Contact_mimeType.equals(ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)) {

                if (contactBook != null) {
                    contactBook.addEmail(contact_type, contact_data);
                }

            } else {
                if (contactBook == null) {
                    //list  contact add to avoid duplication
                    //load All contacts fro device
                    //to add contacts number with name add one extra veriable in ContactBook as number and pass contact_data this give number to you (contact_data is PHONE NUMBER)
                    contactBook = new ContactBook(contact_id, contact_name, getResources(), photo_uri, contact_acc_type, "phone number");
                    addressbook_array.put(contact_id, contactBook);
                    listview_address.add(contactBook);

                }
               // contactBook.addPhone(contact_type, contact_data);


            }


        }

        cursor.close();


        try {
            Collections.sort(listview_address, new Comparator<ContactBook>() {
                @Override
                public int compare(ContactBook lhs, ContactBook rhs) {
                    return lhs.name.toUpperCase().compareTo(rhs.name.toUpperCase());
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

您可以在上面我评论过的代码中使用以下代码。它将单个联系人与其多个号码组合在一起。要获取与单个联系人关联的所有号码,请使用 Object 类中的数组。

 if (contactBook == null) {
            //irst  contact add to avoid duplication
            //load All contacts fro device
            contactBook = new ContactBook(contact_id, contact_name, getResources(), photo_uri, contact_acc_type, "");
            addressbook_array.put(contact_id, contactBook);
            listview_address.add(contactBook);
        }

        String Contact_mimeType = cursor.getString(mimeTypeIdx);
        //here am checking Contact_mimeType to get mobile number asociated with perticular contact and email adderess asociated
        if (Contact_mimeType.equals(ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)) {

            contactBook.addEmail(contact_type, contact_data);

        } else {

            contactBook.addPhone(contact_type, contact_data);

        }

对象类

public class ContactBook {
public int id;
public Resources res;
public String name;
public String photo;
public String contact_acc_type;
public SparseArray<String> emails;
public SparseArray<String> phones;
/*  public LongSparseArray<String> emails;
public LongSparseArray<String> phones;*/
public String header = "";


public ContactBook(int id, String name, Resources res, String photo, String contact_acc_type, String header) {
    this.id = id;
    this.name = name;
    this.res = res;
    this.photo = photo;
    this.contact_acc_type = contact_acc_type;
    this.header = header;
}

@Override
public String toString() {
    return toString(false);
}

public String toString(boolean rich) {

    //testing method to check ddata
    SpannableStringBuilder builder = new SpannableStringBuilder();
    if (rich) {
        builder.append("id: ").append(Long.toString(id))
                .append(", name: ").append("\u001b[1m").append(name).append("\u001b[0m");
    } else {
        builder.append(name);
    }

    if (phones != null) {
        builder.append("\n\tphones: ");
        for (int i = 0; i < phones.size(); i++) {
            int type = (int) phones.keyAt(i);
            builder.append(ContactsContract.CommonDataKinds.Phone.getTypeLabel(res, type, ""))
                    .append(": ")
                    .append(phones.valueAt(i));
            if (i + 1 < phones.size()) {
                builder.append(", ");
            }
        }
    }

    if (emails != null) {
        builder.append("\n\temails: ");
        for (int i = 0; i < emails.size(); i++) {
            int type = (int) emails.keyAt(i);
            builder.append(ContactsContract.CommonDataKinds.Email.getTypeLabel(res, type, ""))
                    .append(": ")
                    .append(emails.valueAt(i));
            if (i + 1 < emails.size()) {
                builder.append(", ");
            }
        }
    }
    return builder.toString();
}

public void addEmail(int type, String address) {
    //this is the array in object class where i am storing contact all emails of perticular contact (single)
    if (emails == null) {
        //   if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
        emails = new SparseArray<String>();
        emails.put(type, address);
        /*} else {
            //add emails to array below Jelly bean //use single array list
        }*/
    }
}

public void addPhone(int type, String number) {
    //this is the array in object class where i am storing contact numbers of perticular contact
    if (phones == null) {
        //  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
        phones = new SparseArray<String>();
        phones.put(type, number);
       /* } else {
            //add emails to array below Jelly bean //use single array list
        }*/
    }
}}
于 2017-09-29T13:12:31.823 回答
5

为了以最少的时间加载联系人,最佳解决方案是在查询光标的联系人时使用投影和选择参数的概念。

这可以通过以下方式完成

void getAllContacts() {
    long startnow;
    long endnow;

    startnow = android.os.SystemClock.uptimeMillis();
    ArrayList arrContacts = new ArrayList();

    Uri uri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI;
    String selection = ContactsContract.Contacts.HAS_PHONE_NUMBER;
    Cursor cursor = ctx.getContentResolver().query(uri, new String[]{ContactsContract.CommonDataKinds.Phone.NUMBER,   ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME, ContactsContract.CommonDataKinds.Phone._ID, ContactsContract.Contacts._ID}, selection, null, ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME + " ASC");

    cursor.moveToFirst();
    while (cursor.isAfterLast() == false) {

        String contactNumber = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
        String contactName = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
        int phoneContactID = cursor.getInt(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone._ID));
        int contactID = cursor.getInt(cursor.getColumnIndex(ContactsContract.Contacts._ID));
        Log.d("con ", "name " + contactName + " " + " PhoeContactID " + phoneContactID + "  ContactID " + contactID)

        cursor.moveToNext();
    }
    cursor.close();
    cursor = null;

    endnow = android.os.SystemClock.uptimeMillis();
    Log.d("END", "TimeForContacts " + (endnow - startnow) + " ms");
}

使用上述方法,加载联系人需要 400 毫秒(不到一秒),而正常情况下需要 10-12 秒。

有关详细信息,这篇文章可能会有所帮助,因为我从中获得了帮助 http://www.blatin.in/2016/02/loading-contacts-fast-from-android.html

于 2016-02-19T10:55:42.523 回答
4

如果您的时间随着您的数据而增加,那么您可能正在运行一个新查询来获取每个联系人的电话/电子邮件。如果您使用 查询电话/电子邮件字段ContactsContract.CommonDataKinds.Phone.NUMBER,那么您将只检索每个联系人的 1 部电话。解决方案是投影字段并通过联系人 ID 加入它们。

这是我在 Kotlin 中的解决方案(提取 id、姓名、所有电话和电子邮件):

val projection = arrayOf(
        ContactsContract.Data.MIMETYPE,
        ContactsContract.Data.CONTACT_ID,
        ContactsContract.Contacts.DISPLAY_NAME,
        ContactsContract.CommonDataKinds.Contactables.DATA

)
val selection = "${ContactsContract.Data.MIMETYPE} in (?, ?)"

val selectionArgs = arrayOf(
        ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE,
        ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)

val contacts = applicationContext
        .contentResolver
        .query(ContactsContract.Data.CONTENT_URI, projection, selection, selectionArgs, null)
        .run {
            if (this == null) {
                throw IllegalStateException("Cursor null")
            }
            val contactsById = mutableMapOf<String, LocalContact>()
            val mimeTypeField = getColumnIndex(ContactsContract.Data.MIMETYPE)
            val idField = getColumnIndex(ContactsContract.Data.CONTACT_ID)
            val nameField = getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME)
            val dataField = getColumnIndex(ContactsContract.CommonDataKinds.Contactables.DATA)

            while (moveToNext()) {
                val mimeType = getString(mimeTypeField)
                val id = getString(idField)
                var contact = contactsById[id]
                if (contact == null) {
                    val name = getString(nameField)
                    contact = LocalContact(id = id, fullName = name, phoneNumbers = listOf(), emailAddresses = listOf())
                }
                val data = getString(dataField)
                when(getString(mimeTypeField)) {
                    ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE ->
                        contact = contact.copy(emailAddresses = contact.emailAddresses + data)
                    ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE ->
                        contact = contact.copy(phoneNumbers = contact.phoneNumbers + data)
                }
                contactsById[id] = contact
            }
            close()
            contactsById.values.toList()
        }

作为参考,我的LocalContact模型:

data class LocalContact(
        val id: String,
        val fullName: String?,
        val phoneNumbers: List<String>,
        val emailAddresses: List<String>
)
于 2020-03-19T13:28:13.147 回答
0

我认为这是一个更好的解决方案:

public ContentValues getAllContacts() {
    ContentValues contacts = new ContentValues();
    ContentResolver cr = getContentResolver();
    Cursor cur = cr.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);

    if (cur != null && cur.getCount() > 0) {
        while (cur.moveToNext()) {
            String id = cur.getString(cur.getColumnIndex(ContactsContract.Contacts._ID));
            String name = cur.getString(cur.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));

            if (cur.getInt(cur.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER)) > 0) {
                Cursor pCur = cr.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null,
                        ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ?", new String[]{id}, null);
                if (pCur != null) {
                    while (pCur.moveToNext()) {
                        String phoneNo = pCur.getString(pCur.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
                        contacts.put(phoneNo, name);
                    }
                    pCur.close();
                }
            }
        }
        cur.close();
    }
    return contacts;
}

要使用它,您需要调用此行一次

ContentValues contacts = new ContentValues();
contacts = getAllContacts();

当您想按号码获取联系人姓名时,只需使用:

String number = "12345";
String name = (String) G.contacts.get(number);

这个算法有点快...

于 2017-07-29T04:45:09.830 回答