Grokking Android

Getting Down to the Nitty Gritty of Android Development

Android’s ContentProviderOperation: “withBackReference” explained

By 14 Comments

The class ContentProviderOperation.Builder has a method named withValueBackReference(String key, int previousResult). The API states ominously “Add a ContentValues back reference. A column value from the back references takes precedence over a value specified in withValues(ContentValues)”. Now, thank you, Google! That about explains everything!

Android’s documentation is never short of surprises. A lot of stuff is explained wonderfully but from time to time there are methods or members that sorely lack a decent documentation. This is an example of the latter. The sad thing is, that this method is very useful and understanding it makes using content providers that much easier and faster.

So let me shed some light on this method. If you are using a bunch of ContentProviderOperations chances are that you might want to use the id of an entry you inserted in a previous operation. That is where this method comes into play. Say you want to add a contact. You have to add a row to RawContacts and you have to add at least one row to the data table with additional information to your contact. Say we add an email, a birthday and a phone number.

The list of operations would look like this:

  1. Insertion of raw contact
  2. Insertion of email
  3. Insertion of birthday
  4. insertion of phone number

In this list every operation after the first one has to use the id of the raw contact to which the information should be added. But this id doesn’t exist prior to the insertion into the raw contacts table in the first operation. So you have to take the value of the first operation. But you don’t know this value when creating the list of ContentProviderOperation objects. That’s what this method is about. The first parameter states the column within the current operation for which you want to use a previous result. And the second parameter is the index of the previous operation of which you want to use a value. In our case it’s the id from the first operation. As usual with Java the index of the operations begins at zero.

A lot of description. Now let me show you some code:

ops = new ArrayList<ContentProviderOperation>();
   .withValue(RawContacts.ACCOUNT_TYPE, someAccountType)
   .withValue(RawContacts.ACCOUNT_NAME, someAccountName)
   .withValueBackReference(Data.RAW_CONTACT_ID,  0)
   .withValue(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE)
   .withValue(StructuredName.DISPLAY_NAME, displayName)
   .withValue(StructuredName.FAMILY_NAME, lastName)
   .withValue(StructuredName.GIVEN_NAME, firstName)
   .withValueBackReference(Data.RAW_CONTACT_ID,  0)
   .withValue(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE)
   .withValue(Phone.NUMBER, phoneNumber)
   .withValue(Phone.TYPE, Phone.TYPE_HOME)
   .withValue(Phone.LABEL, label)
   .withValueBackReference(Data.RAW_CONTACT_ID, 0)
   .withValue(Data.MIMETYPE, Event.CONTENT_ITEM_TYPE)
   .withValue(Event.TYPE, Event.TYPE_BIRTHDAY)
   .withValue(Event.START_DATE, dateStr)
try {
         applyBatch(ContactsContract.AUTHORITY, ops);
} catch (RemoteException e) {
   // some error handling
} catch (OperationApplicationException e) {
   // some error handling

Now you might wonder: With inserting an element the return value is a URI but what you need within those other operations is the id as a long value. How does this work? Gladly Android does this for you as well. For URI values it uses ContentUris.parseId(Uri) to get the id. For all other values it takes an int – e.g. for the number of deleted records as a result of a delete operation.

You see, it’s actually quite simple. If only Android’s developers had it explained properly 🙁

What other good examples of cryptic documentation are out there? Which would you like to get explained in more detail?

Wolfram Rittmeyer lives in Germany and has been developing with Java for many years.

In recent years he shifted his attention to Android and blogs about anything interesting that came up while developing for Android.

You can find him on Google+ and Twitter.

14 thoughts on “Android’s ContentProviderOperation: “withBackReference” explained”

  1. Hey Wolfram,

    I love your blog… thanks a lot for clarifying these things. I just began reading up on SyncAdapters two days ago and I also found that the documentation really lacks in those areas. That said, I have found that the way it all integrates with the Android system is quite magical. Thanks again!


    1. Wolfram Rittmeyer

      Alex, thanks! Could you please add some more hints as to what is missing? I might cover them in future posts.

      I also plan a post about sync adapters very soon – maybe in two weeks time. But probably too late for you.

      1. Well, I managed to write the SyncAdapter and it appears to auto-update and perform syncs as it should.

        The main thing that I find lacking in the documentation is how to cover the edge cases (i.e. unauthorized HTTP requests, IOExceptions, etc.) and how to appropriately report these errors back to the SyncAdapter. Some of this stuff is hard to test (i.e. if the access token is revoked and an HTTP 401 response status is returned, I know that you should call “invalidateAuthToken”, but what comes after that?). This might be way too specific for a simple tutorial, so I might have to just go about figuring it out myself. It is somewhat annoying though that there isn’t an article on the developers site though…

        It won’t be too late… although my implementation is probably not 100% robust as of right now, I’ll probably spend more time on the UI and additional features until then.

        Thanks again!

        1. Wolfram Rittmeyer

          Thanks, Alex, for your answer.

          I guess I will only touch on these topics – if at all – in my initial, tutorial style post. But I always tend to surround these kind of posts with advanced ones. In these I will definitely cover more detailed questions about sync adapters and thus one or two of your edge cases.

  2. Hi Wolfram,
    Thanks for this useful post.
    I have a question about ‘withBackReference’, what if the uri for the current operation depends on the id returned by the previous operation?

    1. You cannot use withValueBackReference() for this. You have to construct the URI yourself using ContentUris.withAppendedId(). But you still should use ContentProviderOperation. See my post on how to use ContentProviderOperation.

      1. Thanks for quick reply.
        I already read this post, but how to use it while the URI for the current operation depends on the previous operation result, i should got the id to construct the URI, but i didn’t find any way to get it with ContentProviderOperation.
        Do you have any suggestion?

        1. Sorry, Rehab. Didn’t get your question the first time. What I don’t understand is why you need the id to build a URI? You have different paths but use the same ids?

          1. Yes, i have 4 operations(accessing 4 tables), 3 of them depends on the result of the first one (the URI for each contain the id of the first one), then this process repeated for more rows, so i need to make this with ContentProviderOperration to be faster.

  3. looked at the source code for the ContentProvider.applyBach(), it make a loop over the operations and call apply() for each operation separately and not in one transaction!!
    this is the link for this part in the source code:

    1. Yes. You are right. By default no transaction is used – but it is still faster, since no context switches are necessary.

      And the Android standard content providers (e.g. ContactsContract and CalendarContract) do use transactions for applyBatch!

      Finally: It is easy to add transactional support to your own ContentProviders by overriding applyBatch():
      1. Start the transaction
      2. Iterate through all operations
      2.1. Yield for operations that are marked as “isYieldAllowed()
      2.2. Apply the operation
      3. Set the transaction to succesful
      4. End the transaction

      1. Check out the Google I/O app to see how they have overridden applyBatch() to work with a single transaction.

        1. You’re right. I should update the code. For what it’s worth: I’m doing more or less the same as the IO app in my ContentProvider sample app on bitbucket.

  4. Events are not getting inserted.

Leave a Reply

Your email address will not be published. Required fields are marked *