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:
- Insertion of raw contact
- Insertion of email
- Insertion of birthday
- 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>();
ops.add(ContentProviderOperation.
newInsert(RawContacts.CONTENT_URI)
.withValue(RawContacts.ACCOUNT_TYPE, someAccountType)
.withValue(RawContacts.ACCOUNT_NAME, someAccountName)
.build());
ops.add(ContentProviderOperation.
newInsert(ContactsContract.Data.CONTENT_URI)
.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)
.build());
ops.add(ContentProviderOperation.
newInsert(ContactsContract.Data.CONTENT_URI)
.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)
.build());
ops.add(ContentProviderOperation.
newInsert(ContactsContract.Data.CONTENT_URI)
.withValueBackReference(Data.RAW_CONTACT_ID, 0)
.withValue(Data.MIMETYPE, Event.CONTENT_ITEM_TYPE)
.withValue(Event.TYPE, Event.TYPE_BIRTHDAY)
.withValue(Event.START_DATE, dateStr)
.build());
try {
getContentResolver().
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?