Sunday 31 January 2010

Data Driven ContentProvider for Android

Recently I have been playing with Google's Android platform for mobile devices. The application I am working on required a data store, thankfully android provides access to the excellent SQLite database engine. Indeed it even provides a selection of utility wrappers for working with databases without the need to craft any SQL statements. A handy feature for those with no prior knowledge of, or wish to use, SQL directly. One such mechanism is the ContentProvider class. In android ContentProviders are used to provide shared access to various data stores that may exist on the device, or possibly remote sources via a network link. Abstracting the specific data source behind a standardised interface results in a powerful tool for sharing or collecting data by an application.



As such a useful way of working with data is likely to crop up a lot, some kind of generic component seemed in order. Something that can be dropped into an application and configured to work in the way it needs with the minimum amount of hassle and time. Thankfully it seems this is entirely possible, at least for the most simple common case of database usage. That is, assuming that a single item will be described by a single row in a given table, such that each column holds one of that items properties. Quite a typical usage pattern and common enough to merit spending the time to write this over just hard coding access ( ah software engineers, how we love to spend 4 hours making a 3 hour job take only 2 hours ).



So after a weekends playing with the code I came up with a GenericProvider class that can be configured entirely via XML data. This class makes two main assumptions about how the data will be presented. Firstly, it assumes one item per row, as described above. Secondly it assumes that each element will have a unique primary key column called "_id", this is recommended by the Android documentation so its not really an issue.



How to use it, firstly three entries need to be added to res/values/strings.xml:




<string name="db_name">MyShinyDB</string>
<string name="db_version">1</string>
<stringname="authority">my.app.AppName.AppContent</string>


These strings give us the name for our database, its version number for upgrades and finally the authority string for our provider, see the Android documentation for how this is used. Next in the manifest we declare our provider class:




<provider name=".GenericProvider" authorities="@string/authority"></provider>


Unfortunately this wont work, I'm not sure why but Eclipse thinks this is a syntax error and forces me to explicitly type out the authority string rather than referencing it. So our actual provider declaration becomes:




<provider name=".GenericProvider" authorities="my.app.AppName.AppContent"></provider>


Note that the authority string is still referenced by the code so needs to remain in strings.xml. Finally we create a new file called "database_schema.xml" under res/values/xml. This file describes how our database will look and details the mime string for each item type.




<?xml version="1.0" encoding="utf-8"?>

<!-- a database schemer defined in xml so i can change it easily -->
<database>
<table name="record" >
<column name="_id" type="INTEGER PRIMARY KEY AUTOINCREMENT"></column>
<column name="date" type="DATETIME2"></column>
<column name="name" type="VARCHAR"></column>
<column name="tag1" type="VARCHAR"></column>
<column name="tag2" type="VARCHAR"></column>
<column name="tag3" type="VARCHAR"></column>
<column name="value" type="VARCHAR"></column>
</table>
<table name="todo" >
<column name="_id" type="INTEGER PRIMARY KEY AUTOINCREMENT"></column>
<column name="date" type="DATETIME2"></column>
<column name="due" type="DATETIME2"></column>
<column name="name" type="VARCHAR"></column>
<column name="tag1" type="VARCHAR"></column>
<column name="tag2" type="VARCHAR"></column>
<column name="tag3" type="VARCHAR"></column>
<column name="value" type="VARCHAR"></column>
</table>

<mimes>
<mime table="record" type="app.myapp.record" ></mime>
<mime table="todo" type="app.myapp.todo" ></mime>
</mimes>
</database>



That's that, a simple content provider can now be added to an application in minutes. The code for GenericProvider is available via the final link in this post, please note this is early code and may not be complete in some places, as always, comments welcome.



http://code.google.com/p/android-bits