Grokking Android

Getting Down to the Nitty Gritty of Android Development

Use Android’s ContentObserver in Your Code to Listen to Data Changes

By

When you are using a content provider as a client, chances are that you want to know whenever the data changes. That's what Android's class ContentObserver is for.

To use the ContentObserver you have to take two steps:

Implement a subclass of ContentObserver

ContentObserver is an abstract class with no abstract methods. Its two onChange() methods are implemented without any logic. And since these are called whenever a change occurs, you have to override them.

Since Google added one of the two overloaded onChange() methods as recently as API-level 16, this method's default behavior is to call the other, older method.

Here is, what a normal implementation would look like:


@SuppressLint("NewApi")
class MyObserver extends ContentObserver {		
   public MyObserver(Handler handler) {
      super(handler);			
   }

   @Override
   public void onChange(boolean selfChange) {
      this.onChange(selfChange, null);
   }		

   @Override
   public void onChange(boolean selfChange, Uri uri) {
      // do s.th.
      // depending on the handler you might be on the UI
      // thread, so be cautious!
   }		
}

Some things are important with the above code. The first thing you must know, is that the second method is only available from API level 16 onwards. That's why I added the SuppressLint annotation. The code works fine on older devices, but in this case Android obviously always calls the old one. So your code should not rely on a URI to work properly.

Also notice the Handler parameter in the constructor. This handler is used to deliver the onChange() method. So if you created the Handler on the UI thread, the onChange() method will be called on the UI thread as well. In this case avoid querying the ContentProvider in this method. Instead use an AsyncTask or a Loader.

If you pass a null value to the constructor, Android calls the onChange() method immediately - regardless of the current thread used. I think it's best to always use a handler when creating the ContentObserver object.

Register your content observer to listen for changes

To register your ContentObserver subclass you simply have to call the ContentResolver's registerContentObserver() method:


getContentResolver().
      registerContentObserver(
            SOME_URI, 
            true, 
            yourObserver);

It takes three parameters. The first is the URI to listen to. I cover the URI in more detail in the next section.

The second parameter indicates whether all changes to URIs that start with the given URI should trigger a method call or just changes to exactly this one URI. This can be handy for say the ContactsContract URI with its many descendants. But it can also be detrimental in that the actual change, that caused the method call, is even more obscure to you.

The third parameter is an instance of your ContentObserver implementation.

The URIs you can observe

As described in my introduction to content providers content URIs can be directory-based or id-based.

Both of these URI-types can be used for your content observer. If you have a detail screen you would use an id-based URI for your observer, and when you use a list of data a directory-based URI is more appropriate.

This does not always work, though. ContactsContract for example always triggers a change, whenever any contact was changed, even if you are listening to a more specific URI. It depends on the correct implementation of the content provider. I have filed a bug report for the ContactsContract provider. Please vote for this issue, if you agree.

When you write a content provider for your app, take care of notifying the correct URI. Only if you do so, the feedback mechanism described here works. This is important for your observers - or if the provider is exported for your clients' observers as well. And it is also important for Loaders. See my post about how to write content providers to learn more about this.

Note: If you use Loaders you do not need to listen to changes yourself. In this case Android registers a ContentObserver and triggers your LoaderCallbacks onLoadFinished() method for any changes.

Do not forget to unregister your content observer

When you have registered a content observer, it is your responsibility to also unregister it. Otherwise you would create a memory leak and your Activity would never be garbage collected.

To unregister you call the unregisterContentObserver() method of the ContentResolver:


getContentResolver().
      unregisterContentObserver(yourObserver);

You register your observer in the onResume() lifecycle method and you unregister it in the onPause() method.

This is not relevant to ContentObservers alone but applies to everything you register. As a general rule of thumb: Whenever you start coding registerXYZ() immediately also add the code to unregisterXYZ(). Otherwise you might later forget about it and inadvertently create memory leaks.

Sometimes you wish you would know more about the changes

The main downside with content observers is, that you do not get any additional information about what has changed. For directory-based URIs it would be nice to get a list of IDs that have changed. Some providers can have many records and re-reading all records, if only a few have changed, is a waste of resources in a mobile environment. Also some hint of the type of change would be nice. Like if a record was deleted or inserted.

The ContentProvider responsible for the change knows all this. So this would be no problem to add. Of course it has to be added in a backwards-compatible way.

API level 16 added a URI to the onChange() method, but this isn't sufficient for updates of multiple records and also doesn't tell you anything about the type of change.

Wrapping up

This quick tip showed you how to leverage Android's ContentObserver. This makes it easy to react to updates of the data you are using.

As you have seen, it is pretty easy to implement the ContentObserver subclass. You have also seen that you can register it to listen to id-based as well as directory-based URIs.

Of course this works only, if the content provider for the URI you are observing, is correctly implemented and notifies clients of changes. If you are implementing a content provider yourself, have a look at my tutorial on writing a content provider for how to do so.

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

He has been interested in Android for quite a while and has been blogging about all kind of topics around Android.

You can find him on Google+ and Twitter.