优化 contentProvider 查询以检索联系人姓名和电话

发布于 2024-12-06 04:45:06 字数 1438 浏览 1 评论 0 原文

目前,我正在使用此代码来获取联系人姓名和电话号码:

    ContentResolver contentResolver = getContentResolver();

    Cursor people = contentResolver.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);

    int nameIndex = people.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME);
    int idIndex = people.getColumnIndex(ContactsContract.Contacts._ID);
    int hasPhoneNumberIndex = people.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER);

    String name, id;
    int hasPhoneNumber;

    while(people.moveToNext()){
        name = people.getString(nameIndex);
        id = people.getString(idIndex);
        hasPhoneNumber = people.getInt(hasPhoneNumberIndex);

        if(hasPhoneNumber > 0){
            Cursor phones = contentResolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, ContactsContract.CommonDataKinds.Phone.CONTACT_ID +" = "+id, null, null);
            phones.moveToFirst();

            int phoneIndex = phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);
            String phone = phones.getString(phoneIndex);

            HashMap<String, String> namePhoneType = new HashMap<String, String>();
            namePhoneType.put("Name", name);
            namePhoneType.put("Phone", phone);

            m_peopleList.add(namePhoneType);

            phones.close();
        }
    }

但这非常慢。

有没有一种方法可以仅在一次查询中检索姓名和电话?

Currently, I'm using this code in order to get the contact name and the phone number:

    ContentResolver contentResolver = getContentResolver();

    Cursor people = contentResolver.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);

    int nameIndex = people.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME);
    int idIndex = people.getColumnIndex(ContactsContract.Contacts._ID);
    int hasPhoneNumberIndex = people.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER);

    String name, id;
    int hasPhoneNumber;

    while(people.moveToNext()){
        name = people.getString(nameIndex);
        id = people.getString(idIndex);
        hasPhoneNumber = people.getInt(hasPhoneNumberIndex);

        if(hasPhoneNumber > 0){
            Cursor phones = contentResolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, ContactsContract.CommonDataKinds.Phone.CONTACT_ID +" = "+id, null, null);
            phones.moveToFirst();

            int phoneIndex = phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);
            String phone = phones.getString(phoneIndex);

            HashMap<String, String> namePhoneType = new HashMap<String, String>();
            namePhoneType.put("Name", name);
            namePhoneType.put("Phone", phone);

            m_peopleList.add(namePhoneType);

            phones.close();
        }
    }

But this is extremely slow.

Is there a way to retrieve name and phone in only one query?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

宁愿没拥抱 2024-12-13 04:45:06

在我看来,所提到的性能问题源于所提出的实现中固有的“n+1 选择”问题。

  1. 打开游标以迭代所有联系人 (1)
  2. 对于每个联系人 打开游标以迭代该联系人的电话号码 (n)

如果您确实需要所有这些数据,更快的方法是从联系人和电话执行两个查询返回适当的代理键和主键的数字,然后在内存中执行连接。

查询 1:通过 ContactId 将所有联系人获取为地图

With the myriad of solutions proposed being sure to pull out the _ID field as the ContactId

查询 2:获取所有电话号码并将它们存储在列表中

        Cursor c = MyO2Application.getContext().getContentResolver().query(
                ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
                new String[] {
                        ContactsContract.CommonDataKinds.Phone._ID,
                        ContactsContract.CommonDataKinds.Phone.CONTACT_ID,
                        ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME,
                        ContactsContract.CommonDataKinds.Phone.NUMBER },
                null,
                null,
                null
        );
        while (c.moveToNext()) {
            String id = c.getString(0);
            String contactId = c.getString(1);  // this is the foreign key to the contact primary key
            String displayName = c.getString(2);
            String number = c.getString(3);

然后您可以迭代电话号码列表,通过 ContactId 从地图中查找联系人并将电话号码关联起来与联系。

1000 个联系人的执行速度从 60 秒降至 4 秒。通常情况下,需要在内存消耗和对 GC 的影响之间进行权衡。

发布时的观察:将 ids 作为 int 获取并使用 SparseArray 可能是一种值得考虑的方法。然而,与此处解决的“n+1 选择”问题相比,预计影响很小。

It seems to me that the noted performance issue stems from the inherent "n+1 select" problem in the implementations proposed.

  1. open a cursor to iterate over all contacts (1)
  2. for each contact open a cursor to iterate over the phone numbers for that contact (n)

A faster approach, if you truly need all this data, is to perform two queries from contacts and phone numbers returning the appropriate surrogate and primary keys and then performing the join in memory.

Query 1: get all contacts as a Map by ContactId

With the myriad of solutions proposed being sure to pull out the _ID field as the ContactId

Query 2: get all the phone numbers and store them in a list

        Cursor c = MyO2Application.getContext().getContentResolver().query(
                ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
                new String[] {
                        ContactsContract.CommonDataKinds.Phone._ID,
                        ContactsContract.CommonDataKinds.Phone.CONTACT_ID,
                        ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME,
                        ContactsContract.CommonDataKinds.Phone.NUMBER },
                null,
                null,
                null
        );
        while (c.moveToNext()) {
            String id = c.getString(0);
            String contactId = c.getString(1);  // this is the foreign key to the contact primary key
            String displayName = c.getString(2);
            String number = c.getString(3);

You can then iterate through the list of phone numbers, looking up the contact from the map by ContactId and associate the phone numbers with the contact.

Execution speeds for 1000 contacts went from 60 seconds down to 4 seconds. As is often the case, there is a trade-off on memory consumption and impact to GC.

Observation while posting: getting the ids as an int and using SparseArray may be an approach worth considering. Minimal impact expected, however, compared to the "n+1 select" issue addressed here.

恰似旧人归 2024-12-13 04:45:06

您可以阅读有关如何以不同方式执行此操作的更多信息 这里

这是一个片段

//query for the people in your address book
Cursor cursor = getContentResolver().query(People.CONTENT_URI, null, null, null,People.NAME + " ASC");
startManagingCursor(cursor);

//bind the name and the number fields
String[] columns = new String[] { People.NAME, People.NUMBER };
int[] to = new int[] { R.id.name_entry, R.id.number_entry };
SimpleContactAdapter mAdapter = new SimpleContactAdapter(this, R.layout.list_entry, cursor, columns, to);
this.setListAdapter(mAdapter);

You can read more about how to do it in a different way here

Here's a snippet

//query for the people in your address book
Cursor cursor = getContentResolver().query(People.CONTENT_URI, null, null, null,People.NAME + " ASC");
startManagingCursor(cursor);

//bind the name and the number fields
String[] columns = new String[] { People.NAME, People.NUMBER };
int[] to = new int[] { R.id.name_entry, R.id.number_entry };
SimpleContactAdapter mAdapter = new SimpleContactAdapter(this, R.layout.list_entry, cursor, columns, to);
this.setListAdapter(mAdapter);
往日 2024-12-13 04:45:06

我找到了一种方法:

Cursor people = getContentResolver()
.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
new String[] {Phone._ID, Phone.DISPLAY_NAME, Phone.NUMBER}, null, null,  Phone.DISPLAY_NAME + " ASC");

I've founded a way:

Cursor people = getContentResolver()
.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
new String[] {Phone._ID, Phone.DISPLAY_NAME, Phone.NUMBER}, null, null,  Phone.DISPLAY_NAME + " ASC");
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文