获取 Android 中的所有联系人数据 - 多个 URI

Posted

技术标签:

【中文标题】获取 Android 中的所有联系人数据 - 多个 URI【英文标题】:Get all contact data in Android - multiple URI 【发布时间】:2014-02-18 05:15:11 【问题描述】:

我正在尝试一次性获取所有联系人数据。 我需要 : * 名 * 姓 * 电话号码 * 版本 等等

为此,我正在对整个联系人进行查询,然后,对于每个联系人 ID,我都会获取相关数据。

问题在于它需要很多时间,大约 400 个联系人需要超过 10 秒。 (在三星 i9300 上运行 4.4.2)

我不需要向用户显示任何内容,我只是在后台将联系人与我的服务器同步,所以我没有任何 UI 问题... 只需遍历联系人,看看需要如何更新并将他添加到我的 ArrayList, 然后 POST 到服务器。

public static void getAllContactsFromDevice() 

        long startTime = System.currentTimeMillis();
        Log.d(TAG, "startTime: " + startTime);

        //  Get all contacts by cursor
        ContentResolver cr = getContext().getContentResolver();
        Cursor cursor = cr.query(ContactsContract.Contacts.CONTENT_URI, null,
                null, null, null);
        if (cursor.moveToFirst() && cursor.getCount() > 0) 
            while (cursor.isAfterLast() == false) 
                // get contact id on the device DB.
                String contactId = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID));

                String contactNumber = null;
                //  Get all phone numbers.
                Cursor phones = cr.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null,
                        ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = " + contactId, null, null);
                // if the user has numbers
                // get just the first one for now
                if (phones.moveToNext())
                    contactNumber = phones.getString(phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
                // close the cursor to prevent leak
                phones.close();

                // if we dont have a phone number, continue to next contact.
                if (contactNumber == null) 
                    cursor.moveToNext();
                    continue;
                

                // get first and last name by contact id
                String[] projection = new String[]ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME, ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME;
                String where = ContactsContract.CommonDataKinds.StructuredName.CONTACT_ID + " = ? AND " + ContactsContract.Data.MIMETYPE + " = ?";
                String[] whereParameters = new String[]contactId, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE;
                Cursor nameCur = cr.query(ContactsContract.Data.CONTENT_URI, projection, where, whereParameters, null);

                String given = null;
                String family = null;
                try 
                    if (nameCur.getCount() > 0) 
                        nameCur.moveToFirst();
                        given = nameCur.getString(nameCur.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME));
                        family = nameCur.getString(nameCur.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME));
                        nameCur.close();
                    
                    // if there is no name, continue to next contact.
                    else 
                        nameCur.close();
                        cursor.moveToNext();
                        continue;
                    
                 catch (Exception e) 
                    e.printStackTrace();
                    nameCur.close();
                


                if (given == null || given == "null")
                    given = "";
                if (family == null || family == "null")
                    family = "";

                String[] mProjection = new String[]ContactsContract.RawContacts.VERSION;
                String[] mWhereParameters = new String[]contactId;
                String mWhere = ContactsContract.CommonDataKinds.StructuredName.CONTACT_ID + " = ? ";
                Cursor mCursor = cr.query(ContactsContract.RawContacts.CONTENT_URI, mProjection, mWhere, mWhereParameters, null);

                int contactVersion = 0;
                try 
                    if (mCursor.getCount() > 0) 
                        mCursor.moveToFirst();
                        contactVersion = mCursor.getInt(mCursor.getColumnIndex(ContactsContract.RawContacts.VERSION));
                        mCursor.close();
                     else 
                        mCursor.close();
                    
                 catch (Exception e) 
                    e.printStackTrace();
                    mCursor.close();
                
                cursor.moveToNext();

            

        
        cursor.close();
        long endTime = System.currentTimeMillis();
        Log.d(TAG, "endTime: " + endTime);
        Log.d(TAG, "total time: " + (endTime - startTime));
    

总时间:18280 毫秒

我查看了HERE developer.android.com,但没有发现任何有用的东西。

this answer 也没有多大帮助。

【问题讨论】:

查看此链接***.com/questions/11991743/… 不,没有帮助,我用它来检查哪些联系人应该与我的服务器联系人同步。我在后台的另一个线程上做... 可以分页吗?先同步一段联系人,然后再滚动同步……这样会减少时间 我没有显示任何内容,只是将所有联系人同步到后台服务器。 【参考方案1】:

问题在于,对于每个联系人,您执行的查询很少:电话号码表、结构化姓名表等。您希望将它们全部批处理并对主要联系人列表执行单个查询,对电话号码列表进行单个查询等.

在拥有约 400 个联系人和完整数据的 Genymotion 上,这种方法可提供 30 倍的性能提升(从 2.8 秒到 100 毫秒)。这是我使用的代码:

private static <TLeft, TRight> Function<Pair<TLeft, TRight>, TLeft> getExtractLeftFunction() 
  return new Function<Pair<TLeft, TRight>, TLeft>() 
    @Override
    public TLeft apply(Pair<TLeft, TRight> input) 
      return input.first;
    
  ;


private static <TLeft, TRight> Map<TLeft, TRight> toMap(Iterable<Pair<TLeft, TRight>> pairs) 
  Map<TLeft, TRight> result = Maps.newHashMap();

  for (Pair<TLeft, TRight> pair : pairs) 
    result.put(pair.first, pair.second);
  

  return result;


private static class Name 
  public final String mGivenName;
  public final String mFamilyName;

  private Name(String givenName, String familyName) 
    mGivenName = givenName;
    mFamilyName = familyName;
  


public void getAllContactsFromDevice() 

  long startTime = System.currentTimeMillis();
  Log.d(TAG, "startTime: " + startTime);

  //  Get all contacts by cursor
  ContentResolver cr = getContentResolver();

  ImmutableList<Long> contactIds = ProviderAction
      .query(Contacts.CONTENT_URI)
      .projection(Contacts._ID)
      .perform(getContentResolver())
      .toFluentIterable(SingleRowTransforms.getColumn(Contacts._ID).asLong())
      .toImmutableList();

  Map<Long, String> phonesMap = toMap(
      ProviderAction.query(Phone.CONTENT_URI)
          .projection(
              Phone.CONTACT_ID,
              Phone.NUMBER
          )
          .whereIn(Phone.CONTACT_ID, contactIds)
          .perform(getContentResolver())
          .toFluentIterable(getToPairFunction(
              SingleRowTransforms.getColumn(Phone.CONTACT_ID).asLong(),
              SingleRowTransforms.getColumn(Phone.NUMBER).asString())
          )
  );

  Map<Long, String> contactsWithPhones = Maps.filterValues(phonesMap, Predicates.notNull());

  Map<Long, Name> contactNamesMap = toMap(ProviderAction
      .query(Data.CONTENT_URI)
      .projection(
          StructuredName.CONTACT_ID,
          StructuredName.FAMILY_NAME,
          StructuredName.GIVEN_NAME
      )
      .whereIn(StructuredName.CONTACT_ID, contactsWithPhones.keySet())
      .perform(getContentResolver())
      .toFluentIterable(getToPairFunction(
          SingleRowTransforms.getColumn(Phone.CONTACT_ID).asLong(),
          new Function<Cursor, Name>() 

            private Function<Cursor, String> mFamilyNameGetter = SingleRowTransforms.getColumn(StructuredName.FAMILY_NAME).asString();
            private Function<Cursor, String> mGivenNameGetter = SingleRowTransforms.getColumn(StructuredName.GIVEN_NAME).asString();

            @Override
            public Name apply(Cursor input) 
              return new Name(
                  mGivenNameGetter.apply(input),
                  mFamilyNameGetter.apply(input)
              );
            
          
      ))
  );

  Map<Long, Integer> versions = toMap(ProviderAction.query(RawContacts.CONTENT_URI)
      .projection(
          StructuredName.CONTACT_ID,
          RawContacts.VERSION
      )
      .whereIn(StructuredName.CONTACT_ID, contactNamesMap.keySet())
      .perform(getContentResolver())
      .toFluentIterable(getToPairFunction(
          SingleRowTransforms.getColumn(StructuredName.CONTACT_ID).asLong(),
          SingleRowTransforms.getColumn(RawContacts.VERSION).asInteger())
      )
  );

  long endTime = System.currentTimeMillis();
  Log.d(TAG, "endTime: " + endTime);
  Log.d(TAG, "total time: " + (endTime - startTime));

我正在使用android-db-commons(免责声明:我是合著者)来执行所有查询并处理输出。该库不支持将查询结果放入 Map (yet!),这是您要在此处执行的操作,所以上面的代码有点乱,但它仍然胜过同时处理多个游标 IMO。

【讨论】:

以上是关于获取 Android 中的所有联系人数据 - 多个 URI的主要内容,如果未能解决你的问题,请参考以下文章

java - 如何使用json中提供的多个联系人数据在java中的android中添加新的电话联系人

Android 来自组和图像的多个联系人

Android - 获取所有联系人列表时出错

如何使用 android 平台获取所有工作资料联系人

从 Android ContactsContract.Contacts 查询所有联系人的许多数据时如何提高性能?

如何从 Android 中的 Intent 中获取联系信息?