The stock database available in Android is
SQLite,
and chances are you’ll find yourself wanting to use it in your
applications. But how do you go about doing so? In this post I’ll go
over some options for accessing the database, exploring both the
standard Android patterns and also an example of plugging in an ORM to
simplify things.
Update (12/13/2011): Updating to work with Mono for Android 4.0
A SQLite database is pretty much exactly what the name implies: it’s a
very lightweight relational database that lends itself well to small
devices since it doesn’t have much overhead. Databases are each
contained to a single file, and you can almost think of it as a
relational facade on top of a file, allowing you to do many things that
you’ve gotten used to doing in your databases: SQL, indices, triggers,
views, etc. I don’t want to too much into the details of the database,
but it should generally handle what you’ll want to do.
SQLiteOpenHelper
The first Android class I want to discuss is
SQLiteOpenHelper.
By extending this class you get an easy hook into the database
creationing and versioning process. By implementing OnCreate and
OnUpgrade, you can easily create and migrate database versions according
to your needs. It will handle things like creating the database if it
doesn’t exist, and running creation/migration in a transaction to keep
things manageable.
For this post, we are going to create an app that lets the user enter
and view notes, each of which consist of a title and the contents of
the note. First let’s create our helper class:
01 | public class NoteDatabaseHelper : SQLiteOpenHelper |
03 | private const string DATABASE_NAME = "Notes"; |
04 | private const int DATABASE_VERSION = 1; |
06 | public NoteDatabaseHelper(Context context) |
07 | : base(context, DATABASE_NAME, null, DATABASE_VERSION) |
11 | public override void OnCreate(SQLiteDatabase db) |
15 | Id INTEGER PRIMARY KEY AUTOINCREMENT, |
17 | Contents TEXT NOT NULL |
21 | public override void OnUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) |
23 | db.ExecSQL("DROP TABLE IF EXISTS Note"); |
You can see that we maintain a constant for the database version so
that it can be easily updated in future versions. For the sake of a
simple demo, the OnUpgrade method just drops the database if it already
exists, but I wouldn’t recommend that for production apps. OnCreate
creates a simple Note table to store our data. You could optionally put
your data access methods in this class as well, but I like to keep them
separate to keep things more organized. This class is best used to just
simply handle your schema management.
The Application
Before we can move on, let’s get the application classes out of the way. First, our model class:
3 | public long Id { get; set; } |
5 | public string Title { get; set; } |
7 | public string Contents { get; set; } |
Nothing crazy there, just a POCO for storing a single note. Next, an interface for a note repository:
1 | public interface INoteRepository |
3 | IList<Note> GetAllNotes(); |
4 | long AddNote(string title, string contents); |
For our app we’ll need a way to get a list of all notes, and also add
a new one to the database. Since we only really need one instance of
the repository for an application, we’ll subclass Application and store
the reference there so that activities can use it.
02 | public class DatabaseDemoApplication : Application |
04 | public INoteRepository NoteRepository { get; set; } |
06 | public DatabaseDemoApplication(IntPtr handle, JniHandleOwnership transfer) |
07 | : base(handle, transfer) |
11 | public override void OnCreate() |
Later, once we create some INoteRepository implementations, we can
plug them in there. For this post I am going to leave out the activity
implementations and their layouts since they’re not anything special or
interesting, but the full source code is available in the sample project
at the end of this post. Instead, let’s move on to what you came here
for: implementing the repository.
The Android Way
The helper class we defined earlier provides access to built-in
functions for accessing the database. You have the option of performing
raw SQL queries, or using some of their managed functions that wrap the
SQL up for you into simple function calls. For this repository, we will
use those managed functions to simplify things. The repository will
internally use an instance of NoteDatabaseHelper to manage everything.
It should be noted that the OnCreate/OnUpgrade methods in the helper do
not actually get called until the first time you reference the
WriteableDatabase or ReadableDatabase properties on the helper class. If
you have things in your create/upgrade methods that take some time,
you’ll want to make sure that gets run up front, rather than when the
user is already in the midst of using the app.
Our repository will be defined as:
01 | public class StandardNoteRepository : INoteRepository |
03 | private NoteDatabaseHelper _helper; |
05 | public StandardNoteRepository(Context context) |
07 | _helper = new NoteDatabaseHelper(context); |
10 | public IList<Note> GetAllNotes() |
13 | _helper.ReadableDatabase.Query("Note", null, null, null, null, null, null); |
14 | var notes = new List<Note>(); |
16 | while (noteCursor.MoveToNext()) |
18 | notes.Add(mapNote(noteCursor)); |
24 | public long AddNote(string title, string contents) |
26 | var values = new ContentValues(); |
27 | values.Put("Title", title); |
28 | values.Put("Contents", contents); |
30 | return _helper.WritableDatabase.Insert("Note", null, values); |
33 | private Note mapNote(ICursor cursor) |
37 | Id = cursor.GetInt(0), |
38 | Title = cursor.GetString(1), |
39 | Contents = cursor.GetString(2) |
The managed query functions return an ICursor, which you can then use
to iterate through the results. In some cases you might want to just
return the cursor from your repository and feed it into something like a
SimpleCursorAdapter to keep things simple. In this case I didn’t want
to keep things dependent on an ICursor (so we can swap this out later)
so I opted not to do that. All that’s left is to plug this
implementation into our application and everything is good to go. Just
add this to DatabaseDemoApplication’s OnCreate() method:
1 | NoteRepository = new StandardNoteRepository(this); |
And that’s how you use the stock Android APIs to access a database.
Easy, right? If you’re like me, your next question will probably be “can
I use an ORM to clean things up even more?” and fortunately, the answer
is yes.
The ORM Way
Now let’s create a new INoteRepository, but this time we’ll use an
ORM to do the querying for us. The question is, which ORM do we use? I
typically reach for
NHibernate for
a normal .NET app, but that’s far heavier than I want to use for a
mobile app (though I have heard reports of it working on MonoDroid, for
whatever that’s worth). I finally settled on
sqlite-net, which was pretty much designed for exactly this use case. Another big benefit of this library is that it also works well with
MonoTouch,
so you can get some good code re-use across platforms. It’s very low
overhead, and just requires adding a single C# file to your project and
you’re ready to go.
First, let’s annotate our Note model class a bit to specify that the Id column is the primary key, and auto-increments.
3 | [PrimaryKey, AutoIncrement] |
4 | public long Id { get; set; } |
6 | public string Title { get; set; } |
8 | public string Contents { get; set; } |
Since we kept the data access logic out of NoteDatabaseHelper, we can
reuse that in the new repository without any modifications and still
get all the versioning management from it. The repository is defined as:
01 | public class OrmNoteRepository : INoteRepository |
03 | private NoteDatabaseHelper _helper; |
05 | public OrmNoteRepository(Context context) |
07 | _helper = new NoteDatabaseHelper(context); |
10 | public IList<Note> GetAllNotes() |
12 | using (var database = new SQLiteConnection(_helper.WritableDatabase.Path)) |
20 | public long AddNote(string title, string contents) |
22 | using (var database = new SQLiteConnection(_helper.WritableDatabase.Path)) |
24 | return database.Insert(new Note |
From sqlite-net we get some nice LINQ action for basic queries, and
can still write raw queries if you need to. That said, remember that
since the NoteDatabaseHelper is still referenced in the repository, you
still have access to all the managed query functions if you find they
work better for something. We use the helper to get the full path of the
database instead of having to manage that ourselves, and the ORM just
opens the file from there. Now if you edit DatabaseDemoApplication to
use this class instead of the first repository, you’ll find that things
still work as expected.
One thing you might notice is that we’re not using the ORM for table
creation in the helper class even though it does include a CreateTable()
method, along with some basic migration functionality built in. The
reason for this is that while SQLiteOpenHelper.OnCreate() is running, it
keeps the database file locked. If you try to use the ORM to access the
database while it’s locked, you will get an exception. Of course you
could decide not to use the helper altogether and then this wouldn’t be
an issue, but in this case I didn’t want to lose the other benefits of
the helper. In most cases in my mobile apps the database schema isn’t
too difficult to manage, so while a little annoying, I haven’t found
this to be a real dealbreaker by any means.

Those are the basics of database access in MonoDroid, using both the
standard Android access patterns as well as using a nice .NET ORM for
simplifying things. You can get a full copy of the source code for this
project over at
GitHub.