0

I'm developing a REST application to delete contacts (and I mean Contacts, not Raw Contacts) in bulk. It is taking me more than 3 minutes to delete a batch of ~116 contacts, it's way to much in my opinion. Sometimes it prints Contacts deleted but they are still in the phone's contact list, other times they are really deleted.

Can anyone point me out where is the problem here?

I receive a JSONArray containing the Contact's ID (and a few other details, but only the IDs are used in this method) that shall be deleted.

Here's my delete contact code:

public Boolean deleteBatchContact(final JSONArray jsonArray) throws JSONException, RemoteException, OperationApplicationException
{
    final ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
    final Long startTime = System.currentTimeMillis();

    if(jsonArray.length() != 0) // there must be something in the JSONArray
    {
        for(int i = 0; i < jsonArray.length(); i++)
        {
            final JSONObject jsonContactObject = jsonArray.getJSONObject(i);
            final String contactId = jsonContactObject.getString("id");
            Long id = Long.parseLong(contactId);

            ops.add(ContentProviderOperation.newDelete(ContentUris.withAppendedId(Contacts.CONTENT_URI, Long.parseLong(contactId)))
                    .withYieldAllowed(true)
                    .build());
        }

        try {
            final ContentProviderResult[] cpr = contentResolver.applyBatch(ContactsContract.AUTHORITY, ops);
            final Long endTimeTry = System.currentTimeMillis();
            final Integer numberOfContactsDeleted = cpr.length;

            if(numberOfContactsDeleted > 0)
            {
                Log.d(TAG, numberOfContactsDeleted + " Contacts deleted!");
                final Long totalSuccess = endTimeTry-startTime;
                Log.d(TAG, "Total Time (Sucess): " + totalSuccess);

                return true;
            }
            else 
            {
                Log.d(TAG, "Menor ou igual a zero...");
                final Long totalFailed = endTimeTry-startTime;
                Log.d(TAG, "Total Time (No deletes): " + totalFailed);
                return false;
            }

        } catch (Exception e) {
            Log.d(TAG, "deleteBatchContact: " + e.getMessage());
            return false;
        }
    }
    Long endTimeReturnFalse = System.currentTimeMillis();
    Long totalTimeReturnFalse = endTimeReturnFalse - startTime;
    Log.d(TAG, "Total time return false: " + totalTimeReturnFalse);
    return false;
}

And here is where I call the deleteBatchContact method:

else if(type.equalsIgnoreCase("delete"))
    {
        Log.d(TAG, "DELETE REQUEST");
        String strJson = request.getEntityAsText();
        int age = response.getAge();
        Log.d(TAG, "age1: " + age);
        Log.d(TAG, "is commited1? " + response.isCommitted());
        try {
            Log.d(TAG, "Try...");
            JSONArray jsonArray = new JSONArray(strJson);
            if(processDelete(jsonArray) == true)
            {
                Log.d(TAG, "Response -> Deleted with Success!");
                response.setEntity("Deleted with success!", MediaType.TEXT_ALL);
                age = response.getAge();
                Log.d(TAG, "age2: " + age);
                Log.d(TAG, "is commited2? " + response.isCommitted());
            }
            else
            {
                Log.d(TAG, "Response -> No contacts deleted...");
                response.setEntity("No contacts deleted...", MediaType.TEXT_ALL);
                age = response.getAge();
                Log.d(TAG, "age3: " + age);
                Log.d(TAG, "is commited3? " + response.isCommitted());
            }
        } catch (RemoteException e) {
            Log.d(TAG, "processDelete exception: " + e.getMessage());
            e.printStackTrace();
        } catch (JSONException e) {
            Log.d(TAG, "processDelete exception: " + e.getMessage());
            e.printStackTrace();
        } catch (OperationApplicationException e) {
            Log.d(TAG, "processDelete exception: " + e.getMessage());
            e.printStackTrace();
        }

    }

Here is the output:

02-13 01:28:04.484: D/ContactRestlet(17638): DELETE REQUEST
02-13 01:28:04.507: D/ContactRestlet(17638): age1: 0
02-13 01:28:04.507: D/ContactRestlet(17638): is commited1? false
02-13 01:28:04.507: D/ContactRestlet(17638): Try...
02-13 01:30:04.671: D/ContactRestlet(17638): DELETE REQUEST
02-13 01:30:04.671: D/ContactRestlet(17638): age1: 0
02-13 01:30:04.671: D/ContactRestlet(17638): is commited1? false
02-13 01:30:04.671: D/ContactRestlet(17638): Try...
02-13 01:31:07.468: D/ContactList(17638): 116 Contacts deleted!
02-13 01:31:07.468: D/ContactList(17638): Total Time (Sucess): 182911
02-13 01:31:07.468: D/ContactRestlet(17638): Response -> Deleted with Success!
02-13 01:31:07.472: D/ContactRestlet(17638): age2: 0
02-13 01:31:07.472: D/ContactRestlet(17638): is commited2? false
02-13 01:31:07.476: W/System.err(17638): 2014-02-13 01:31:07    -   -   -   8080    DELETE  /contacts   -   1001    21  12590   62837   http://10.17.1.72:8080  Apache-HttpClient/4.3.1 (java 1.5)  -

EDIT: As suggested by @RocketRandom, here is my new code:

    public Boolean deleteBatchContact(final JSONArray jsonArray) throws JSONException, RemoteException, OperationApplicationException
{
    final ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
    final Long startTime = System.currentTimeMillis();

    if(jsonArray.length() != 0) // there must be something in the JSONArray
    {
        StringBuilder query = new StringBuilder(" in {");
        for(int i = 0; i < jsonArray.length(); i++)
        {   
            final JSONObject jsonContactObject = jsonArray.getJSONObject(i);
            final String contactId = jsonContactObject.getString("id");

            query.append(contactId).append(",");
        }
        query.deleteCharAt(query.length()-1);
        query.append("}");

        ops.add(ContentProviderOperation.newDelete(Contacts.CONTENT_URI)
        .withSelection(Contacts._ID + " in { 1 , 2 , 3 , 4 , 5, 6, 7, 8, 9}", null).build()); 

        try {
            final ContentProviderResult[] cpr = contentResolver.applyBatch(ContactsContract.AUTHORITY, ops);
            final Long endTimeTry = System.currentTimeMillis();
            final Integer numberOfContactsDeleted = cpr.length;

            if(numberOfContactsDeleted > 0)
            {
                Log.d(TAG, numberOfContactsDeleted + " Contacts deleted!");
                final Long totalSuccess = endTimeTry-startTime;
                Log.d(TAG, "Total Time (Sucess): " + totalSuccess);

                return true;
            }
            else 
            {
                Log.d(TAG, "Menor ou igual a zero...");
                final Long totalFailed = endTimeTry-startTime;
                Log.d(TAG, "Total Time (No deletes): " + totalFailed);
                return false;
            }
        } catch (Exception e) {
            Log.d(TAG, "deleteBatchContact: " + e.getMessage());
            return false;
        }
    }
    Long endTimeReturnFalse = System.currentTimeMillis();
    Long totalTimeReturnFalse = endTimeReturnFalse - startTime;
    Log.d(TAG, "Total time return false: " + totalTimeReturnFalse);
    return false;
}

And this is the output:

03-06 01:42:10.367: D/ContactList(8925): 1 Contacts deleted!
03-06 01:42:10.367: D/ContactList(8925): Total Time (Sucess): 84

Now it's not deleting any contact, can you spot where's the error? Even tho it said one contact deleted, none as deleted. All those contact ids are valid (they do exist on my contacts table, I've downloaded it from my device and check it up.)

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
dazito
  • 7,740
  • 15
  • 75
  • 117

1 Answers1

1

Edit : Removed earlier suggested solution. It does not work. (Sorry for that)

I checked the contacts provider code and bulk deletion is only supported for raw contacts. For contacts all they do in there implementation is just return and they have a a nice little "TODO" there.

Your original code is the right way to do it. What the provider does when you delete a contact (without the isCallerSyncAdapter flag) is it queries all the raw contacts with that contact id and sets there dirty flag one by one ( So that takes a while)

i tried out at my end :

03-19 13:28:11.620: D/tmp(5912): 188 Contacts deleted!

03-19 13:28:11.620: D/tmp(5912): Total Time (Sucess): 387496

i.e takes around 2 seconds per contact which is horrible. But I don't see a way to make it faster without modifying the Android Contacts ContentProvider

RocketRandom
  • 1,102
  • 7
  • 20
  • as far as I know, Android does not let you do those type of queries straight into the SQL Lite Database. – dazito Mar 04 '14 at 12:33
  • I meant creating a similar query using the API that android provides. The where string can be adjusted to incorporate the above query. Updated post with example. – RocketRandom Mar 05 '14 at 13:26
  • please check my edited question. Now it is not deleting any contact. – dazito Mar 06 '14 at 12:51
  • one mistake I see is that ( ) should be used instead of { }. It was a mistake in my sample code. Edited and corrected it now. – RocketRandom Mar 11 '14 at 11:02
  • It's still not working, I changed the {} to () and still no contacts deleted. And I'm 100% sure the contact IDs I'm trying to delete do exist. – dazito Mar 13 '14 at 18:16
  • but still, the query in the database works just fine, on my device (Galaxy S1 running android 4.4) it just does **not** delete any contact. – dazito Mar 13 '14 at 18:31
  • 1
    I am sorry for the incorrect initial suggestion. I have updated my original post. – RocketRandom Mar 19 '14 at 13:24