Technology

Table Joins/Unions and ContentProviders: Reminderer


Ever since I wrote the new ContentProvider framework, Reminderer had this annoying bug where it wouldn’t auto-refresh the open tasks list whenever you added a new task. You added a task but the darn task wouldn’t show on the list until you rotated the phone.

The first approach was to force reload the Loader every time the fragment gets focus. While this hack works, its just that—a hack. This approach quickly becomes inefficient once the task list gets unduly large (as in hundreds of tasks). The second approach was to use a ContentObserver to listen for changes in the database. Fortunately, for some reason, as I’ll explain shortly, the content observer wouldn’t listen to changes in the table.

Why “fortunately”? Because content observers are automatically created for you if you use a Loader, which I am. (Thanks to GrokkingAndroid for pointing this out.)

The million-dollar question is “Why didn’t the auto-created content observer listen to changes?” The short answer is the task list used one URI (think of these as URLs) while the add-new-task activity used a different URI. Hence, the task list’s observer never received the update. Doh!

So why am I using URIs in this way? As I wrote in a previous post, Android’s content provider framework doesn’t have built-in support for table joins and unions. Fortunately, there’s a really simple workaround: use URIs as different views (a table join or a table union) of the data. The bug came when I didn’t manually update the unrelated URIs.

OK, so how do you update these URIs? The naive solution is to keep a manual list of related URIs. But what if you forget to update this list in the future? There has to be a better way, and fortunately there is: use a base URI for all the different views.

The URIs now look like this:

     public final static Uri TASKS_URI = Uri. parse ( baseUri + TASK_TABLE ) ; 
     public final static Uri TASK_JOIN_REPEAT_URI = Uri. parse ( baseUri + TASK_TABLE + "/views/taskjoinrepeat" ) ; 
     /**
      * Convenience Uri to get a view of open tasks
      */ 
     public final static Uri LOAD_OPEN_TASKS_URI = Uri. parse ( baseUri + TASK_TABLE + "/views/loadopentasks" ) ;

The relationship between URIs is in the base URI: com.frankandrobot.reminderer/tasks

The code to fire the observers now looks like this:

        if (url.getPath().startsWith("/tasks"))
            //update all URIs (and therefore all observers) containing the base URI
            getContext().getContentResolver().notifyChange(TASKS_URI, null);
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s