Introducing Android migrations

Have you ever worked with Rails’ migrations? They make database changes a breeze, don’t they? While every software release doesn’t necessarily involve a migration, when one does happen to make use of one, I’m always pleased on how easily things work out. Whether it’s to add new data or alter existing data structures, Rails migrations make evolving a datastore (be it an RDMBS or NoSQL one like MongoDB) painless.

When I recently found myself altering the data structure of a SQLite database for one of my Android apps, I found myself wishing there was some similar migration mechanism for Android as there is in Rails. Alas, I could fine none, so I did what any other developer would do: I wrote one.

Droid Migrate is a simple command line framework that generates and runs database migrations for your Android apps that use SQLite. A migration is encapsulated by a DBVersion class that contains an up and down method. The up method is called for an upgrade and down for a rollback. What those methods do is entirely up to you.

In addition, Droid Migrate generates a DatabaseHelper class through which you obtain underlying connections to a SQLite instance – this is the canonical way to interact with SQLite in an Android app anyway, but with Droid Migrate, you get a specially enhanced DatabaseHelper that determines which version of a target database instance is the most current and runs the appropriate migrations to bring the database to that version.

Thus, with your newly minted DatabaseHelper class, you can still interact with your app’s database like you would normally, however, by using this class, all migrations are handled for you. Allow me to demonstrate.

I’ve created a simple app that doesn’t interact with any database at this point – it simply creates a ListView that is intended to hold a list of records for viewing. You can find this app on Github if you’d like to follow along. Nevertheless, the app’s main Activity is shown below.

Simple Android app w/o any SQLite logic
<span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<code class='java'><span class='line'><span class="kn">package</span> <span class="n">com</span><span class="o">.</span><span class="na">b50</span><span class="o">.</span><span class="na">db</span><span class="o">.</span><span class="na">ex</span><span class="o">;</span>
</span><span class='line'>
</span><span class='line'><span class="kn">import</span> <span class="nn">android.app.Activity</span><span class="o">;</span>
</span><span class='line'><span class="kn">import</span> <span class="nn">android.os.Bundle</span><span class="o">;</span>
</span><span class='line'><span class="kn">import</span> <span class="nn">android.widget.TextView</span><span class="o">;</span>
</span><span class='line'>
</span><span class='line'><span class="kd">public</span> <span class="kd">class</span> <span class="nc">MainActivity</span> <span class="kd">extends</span> <span class="n">Activity</span> <span class="o">{</span>
</span><span class='line'>
</span><span class='line'>  <span class="kd">protected</span> <span class="kt">void</span> <span class="nf">onCreate</span><span class="o">(</span><span class="n">Bundle</span> <span class="n">savedInstanceState</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>    <span class="kd">super</span><span class="o">.</span><span class="na">onCreate</span><span class="o">(</span><span class="n">savedInstanceState</span><span class="o">);</span>
</span><span class='line'>    <span class="n">setContentView</span><span class="o">(</span><span class="n">R</span><span class="o">.</span><span class="na">layout</span><span class="o">.</span><span class="na">activity_main</span><span class="o">);</span>
</span><span class='line'>    <span class="n">TextView</span> <span class="n">textView</span> <span class="o">=</span> <span class="o">(</span><span class="n">TextView</span><span class="o">)</span> <span class="n">findViewById</span><span class="o">(</span><span class="n">R</span><span class="o">.</span><span class="na">id</span><span class="o">.</span><span class="na">textView1</span><span class="o">);</span>
</span><span class='line'>    <span class="n">textView</span><span class="o">.</span><span class="na">setText</span><span class="o">(</span><span class="s">"This would be a list from a DB if there was a DB"</span><span class="o">);</span>
</span><span class='line'>  <span class="o">}</span>
</span><span class='line'><span class="o">}</span>
</span></code>

What I’d like to do is add the ability to interact with a SQLite database; plus, I’d like to be able to evolve the data model on subsequent releases. This is where Droid Migrate shines.

After I’ve installed Droid Migrate (simply clone or download the code, build it, and put it into your PATH and create new environment variable dubbed DROID_MIGRATE_HOME), I can initialize my app to use Droid Migrate by opening up a terminal in the root of my app and typing:

Initializing Droid Migrate
<span class='line-number'>1</span>
<code class='bash'><span class='line'><span class="nv">$></span> droid-migrate init -d a_catalog
</span></code>

The -d flag specifies the name of my desired database. I can optionally provide a package name via the -p flag if I’d like my newly generated classes in a separate package from my main app.

If you take a look at your app’s code, you should notice a number of new things. First, you’ll see two new classes and a new jar file. The classes are the aforementioned DatabaseHelper and a class dubbed DBVersion1. The newly added jar file in your app’s libs folder contains a few classes that correspond to Droid Migrate’s runtime dependencies – this jar is extremely compact at 4KB.

The DatabaseHelper class is brutally simple:

DatabaseHelper couldn’t be any easier
<span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<code class='java'><span class='line'><span class="kn">package</span> <span class="n">com</span><span class="o">.</span><span class="na">b50</span><span class="o">.</span><span class="na">db</span><span class="o">.</span><span class="na">ex</span><span class="o">;</span>
</span><span class='line'>
</span><span class='line'><span class="kn">import</span> <span class="nn">com.b50.db.ex.R</span><span class="o">;</span>
</span><span class='line'><span class="kn">import</span> <span class="nn">android.content.Context</span><span class="o">;</span>
</span><span class='line'><span class="kn">import</span> <span class="nn">com.b50.migrations.MigrationsDatabaseHelper</span><span class="o">;</span>
</span><span class='line'>
</span><span class='line'><span class="kd">public</span> <span class="kd">class</span> <span class="nc">DatabaseHelper</span> <span class="kd">extends</span> <span class="n">MigrationsDatabaseHelper</span> <span class="o">{</span>
</span><span class='line'>  
</span><span class='line'>  <span class="kd">public</span> <span class="nf">DatabaseHelper</span><span class="o">(</span><span class="n">Context</span> <span class="n">context</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>    <span class="kd">super</span><span class="o">(</span><span class="n">context</span><span class="o">,</span> <span class="n">context</span><span class="o">.</span><span class="na">getString</span><span class="o">(</span><span class="n">R</span><span class="o">.</span><span class="na">string</span><span class="o">.</span><span class="na">database_name</span><span class="o">),</span> <span class="kc">null</span><span class="o">,</span>
</span><span class='line'>      <span class="n">context</span><span class="o">.</span><span class="na">getResources</span><span class="o">().</span><span class="na">getInteger</span><span class="o">(</span><span class="n">R</span><span class="o">.</span><span class="na">integer</span><span class="o">.</span><span class="na">database_version</span><span class="o">),</span>
</span><span class='line'>      <span class="n">context</span><span class="o">.</span><span class="na">getString</span><span class="o">(</span><span class="n">R</span><span class="o">.</span><span class="na">string</span><span class="o">.</span><span class="na">package_name</span><span class="o">));</span>
</span><span class='line'>  <span class="o">}</span>
</span><span class='line'><span class="o">}</span>
</span></code>

This class extends Droid Migrate’s MigrationsDatabaseHelper, which ultimately extends Android’s SQLiteOpenHelper so as I mentioned earlier, you’ve got everything you need to interact with SQLite at your fingertips via DatabaseHelper. If you look closely, you’ll see that this class makes use of a specialized XML file (that is ultimately generated into your R class).

Take a look in the res/values folder and open up the newly created migrations.xml file. It should look something like this:

Related:
1 2 3 Page 1
Notice to our Readers
We're now using social media to take your comments and feedback. Learn more about this here.