0

I saving a around 250 contacts in phonebook. And each contact is taking around 1 second to save in contact list. In my app, I might have to save more than 10,000 contacts. Then it will take hours to save them. So, I want a method to quickly save all the contacts. Your answer would really help me a lot :)

Here is the method I am using with a for loop to save a contact -

public static String saveNewContact(String name, String number, ContentResolver contentResolver){
    ContentValues values = new ContentValues();
    values.put(Contacts.People.NUMBER, number);
    values.put(Contacts.People.TYPE, ContactsContract.CommonDataKinds.Phone.TYPE_CUSTOM);
    values.put(Contacts.People.LABEL, name);
    values.put(Contacts.People.NAME, name);
    Uri dataUri = contentResolver.insert(Contacts.People.CONTENT_URI, values);
    Uri updateUri = Uri.withAppendedPath(dataUri, Contacts.People.Phones.CONTENT_DIRECTORY);
    values.clear();
    values.put(Contacts.People.Phones.TYPE, Contacts.People.TYPE_MOBILE);
    values.put(Contacts.People.NUMBER, number);
    updateUri = contentResolver.insert(updateUri, values);
    return getContactID(updateUri, contentResolver);
}


public static String getContactID(Uri contactUri, ContentResolver contentResolver){
    String id = "";
    Cursor cursor = contentResolver.query(contactUri, null,
            null, null, null);
    if (cursor != null && cursor.moveToFirst()) {
        int idx = cursor.getColumnIndex(ContactsContract.Contacts._ID);
        id = cursor.getString(idx);
    }

    return id;
}

EDIT - Tried this code as well, but the results are same

ArrayList<ContentProviderOperation> cntProOper = new ArrayList<>();
    int contactIndex = cntProOper.size();

    cntProOper.add(ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI)
            .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, null)
            .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, null).build());

    cntProOper.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
            .withValueBackReference(ContactsContract.RawContacts.Data.RAW_CONTACT_ID, contactIndex)
            .withValue(ContactsContract.RawContacts.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
            .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, name)
            .build());

    cntProOper.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, contactIndex)
            .withValue(ContactsContract.RawContacts.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
            .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, number)
            .withValue(ContactsContract.CommonDataKinds.Phone.TYPE, ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE).build()); //Type like HOME, MOBILE etc
    try {
        contentResolver.applyBatch(ContactsContract.AUTHORITY, cntProOper);
    } catch (RemoteException | OperationApplicationException exp) {}
marmor
  • 27,641
  • 11
  • 107
  • 150
Vishal Roy
  • 70
  • 1
  • 10

1 Answers1

0

Ok, many things to mention here...

For your first code, NEVER EVER USE Contacts.People.XX APIs, seriously, for anything anywhere, make sure you do not import anything from People or use any of its APIs, this is a very very old API that had been deprecated many years ago and is even not supported for some devices.

Regarding your second code, lots of bugs and issues there which I tried to fix in my code below, but specifically for your performance requirement, note that you don't have to applyBatch for each and every contact, if you're creating many contacts at once, it's ok to put many operations within your ops ArrayList and apply them all in one go - much faster!

Notes:

  1. This SHOULD give you a 100X boost for cache size of 100, but you can play with that number, just note that if the number is too big you risk getting some Java warning of "Transaction Too Large" and the batch will fail.
  2. Always supply ACCOUNT_TYPE / ACCOUNT_NAME values with something related to your app, I've added to this code two const values MY_ACCOUNT_TYPE / MY_ACCOUNT_NAME that you'll need to define.


ArrayList<ContentProviderOperation> ops = new ArrayList<>();
for (int i = 0; i < 10000; i++) {
    createContact(ops, name, phone);
    if (i % 100 == 0) { // experiment with different batch sizes to achieve best performance times
        try {
            contentResolver.applyBatch(ContactsContract.AUTHORITY, ops);
        } catch (RemoteException | OperationApplicationException e) {
            Log.e(TAG, "error applying batch: ", e);
        }
        ops = new ArrayList<>();
    }
}

private void createContact(ArrayList<ContentProviderOperation> ops, String name, String phone) {

    ops.add(ContentProviderOperation.newInsert(RawContacts.CONTENT_URI)
            .withValue(RawContacts.ACCOUNT_TYPE, MY_ACCOUNT_TYPE)
            .withValue(RawContacts.ACCOUNT_NAME, MY_ACCOUNT_NAME).build());

    int indexOfRawContactIdOperation = ops.size() - 1;

    ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, indexOfRawContactIdOperation)
            .withValue(ContactsContract.Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE)
            .withValue(StructuredName.DISPLAY_NAME, name)
            .build());

    ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, indexOfRawContactIdOperation)
            .withValue(ContactsContract.Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE)
            .withValue(Phone.NUMBER, number)
            .withValue(Phone.TYPE, Phone.TYPE_MOBILE).build());
}
marmor
  • 27,641
  • 11
  • 107
  • 150