Tag Archives: howto

Android: Getting Started with Databases and ContentProviders Part 1

Clone the code from GitHub

Android provides powerful database functionality built on SQLite. If you’re familiar with SQL from web development, you’ll be very comfortable with SQLite. However, what you might find is that it’s very easy to deal directly with the database (especially if you’re coming from a framework like Rails or Symfony).

Whatever language or platform you’re using, it’s always good practice to build an abstraction layer between your app’s code and its database. In this tutorial miniseries, we’ll walk through building a simple app that uses a database abstraction class, MyDatabaseHelper to manage querying, inserting, updating and deleting records from a simple database. In future posts, we’ll look at dealing with multiple tables and joins, and finally explore Android’s own ContentProvider API to abstract our database in a standard way.

Getting started

Start with a new Android application project (I recommend trying out the new Android Studio preview, especially if you’re just starting out with Android), and add the following files:

// src/com/example/MyActivity.java
public class MyActivity extends Activity {
  private MyDatabaseHelper mDatabaseHelper;
 
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
 
    mDatabaseHelper = new MyDatabaseHelper(this);
  }
}
// src/com/example/MyDatabaseHelper.java
public class MyDatabaseHelper extends SQLiteOpenHelper {
  public static final String TABLE_USERS = "users";
  public static final String COL_ID = BaseColumns._ID;
  public static final String COL_NAME = "name";
  public static final String COL_EMAIL = "email";
  public static final String COL_DOB = "date_of_birth";
  private static final String DATABASE_NAME = "my_app.db";
  private static final int DATABASE_VERSION = 1;
 
  public MyDatabaseHelper(Context context) {
    super(context, DATABASE_NAME, null, DATABASE_VERSION);
  }
 
  @Override
  public void onCreate(SQLiteDatabase db) {
    db.execSQL("CREATE TABLE " + TABLE_USERS + " ("
        + COL_ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
        + COL_NAME + " TEXT NOT NULL,"
        + COL_EMAIL + " TEXT,"
        + COL_DOB + " INTEGER"
        + ");");
  }
 
  @Override
  public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    db.execSQL("DROP TABLE IF EXISTS " + TABLE_USERS + ";");
    onCreate(db);
  }
 
  public long insert(String tableName, ContentValues values) {
    return getWritableDatabase().insert(tableName, null, values);
  }
 
  public int update(String tableName, long id, ContentValues values) {
    String selection = COL_ID + " = ?";
    String[] selectionArgs = {String.valueOf(id)};
 
    return getWritableDatabase().update(tableName, values, selection, selectionArgs);
  }
 
  public int delete(String tableName, long id) {
    String selection = COL_ID + " = ?";
    String[] selectionArgs = {String.valueOf(id)};
 
    return getWritableDatabase().delete(tableName, selection, selectionArgs);
  }
}

The advantage of this approach is that MyDatabaseHelper becomes a single point of access to the raw database (following good DRY principles). It also provides the opportunity to validate data before it is written to the database. For example, we have added a NOT NULL constraint on the name column. If we tried to save a record to the database without setting a value for name, our app would crash with a SQLiteConstraintException. Let’s demonstrate this by adding a few users in our Activity, but leaving one of the names blank:

// src/com/example/MyActivity.java
  public void onCreate(Bundle savedInstanceState) {
    // ...    
    addUser(null, null, 0);
    addUser("Joe User", "joe@example.com", 0);
    addUser("Mary Jones", "mary@example.com", 0);
    addUser("Sue Bloggs", "sue@example.com", 0);
  }
 
 private void addUser(String name, String email, long dateOfBirthMillis) {
    ContentValues values = new ContentValues();
    values.put(MyDatabaseHelper.COL_NAME, name);
 
    if (email != null) {
      values.put(MyDatabaseHelper.COL_EMAIL, email);
    }
 
    if (dateOfBirthMillis != 0) {
      values.put(MyDatabaseHelper.COL_DOB, dateOfBirthMillis);
    }
 
    mDatabaseHelper.insert(MyDatabaseHelper.TABLE_USERS, values);
  }

Running this code, your app will crash with the SQLiteConstraintException we expected:

  ERROR/SQLiteDatabase(19185): Error inserting name=null
      android.database.sqlite.SQLiteConstraintException: users.name may not be NULL (code 19)
      at android.database.sqlite.SQLiteConnection.nativeExecuteForLastInsertedRowId(Native Method)
      at android.database.sqlite.SQLiteConnection.executeForLastInsertedRowId(SQLiteConnection.java:775)
      at android.database.sqlite.SQLiteSession.executeForLastInsertedRowId(SQLiteSession.java:788)
      ....

Adding Validations

Using our abstract Database helper class though, we can protect against this and handle the error much more elegantly. To do this, we’ll add some simple validations at the appropriate points in MyDatabaseHelper:

// src/com/example/MyDatabaseHelper.java
  public long insert(String tableName, ContentValues values) throws NotValidException {
    validate(values);
 
    return getWritableDatabase().insert(tableName, null, values);
  }
 
  public int update(String tableName, long id, ContentValues values) throws NotValidException {
    validate(values);
 
    String selection = COL_ID + " = ?";
    String[] selectionArgs = {String.valueOf(id)};
 
    return getWritableDatabase().update(tableName, values, selection, selectionArgs);
  }
 
  // ...
 
  protected void validate(ContentValues values) throws NotValidException {
    if (!values.containsKey(COL_NAME) || values.getAsString(COL_NAME) == null || values.getAsString(COL_NAME).isEmpty()) {
      throw new NotValidException("User name must be set");
    }
  }
 
  public static class NotValidException extends Throwable {
    public NotValidException(String msg) {
      super(msg);
    }
  }

Back in MyActivity, wrap the call to insert() in a try...catch block to capture the NotValidException and show a message to the user:

// src/com/example/MyActivity.java 
  private void addUser(String name, String email, long dateOfBirthMillis) {
    // ...  
    try {
      mDatabaseHelper.insert(MyDatabaseHelper.TABLE_USERS, values);
    } catch (MyDatabaseHelper.NotValidException e) {
      Toast.makeText(this, e.getMessage(), Toast.LENGTH_SHORT).show();
    }
  }

Now if you run your app, it will gracefully handle the error, and continue adding the other user records to the local database.

Querying and Displaying Records

At the moment, we can’t see our data though. We’ll add a quick ListView to MyActivity to show the list of user’s. If one doesn’t already exist, create a new layout resource file in your project: res/layouts/my_activity.xml and add a ListView to fill the screen:

// res/layout/my_activity.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="match_parent">
 
    <ListView
        android:layout_width="fill_parent"
        android:layout_height="match_parent"
        android:id="@+id/listView"/>
</LinearLayout>

In MyActivity, make sure that you set the content view to be the new my_activity.xml layout:

// src/com/example/MyActivity.java
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.my_activity);
 
    // ...
  }

Next, create a SimpleCursorAdapter to retrieve data from the database and display it in our new ListView:

// src/com/example/MyActivity.java
  public void onCreate(Bundle savedInstanceState) {
    // ...
 
    Cursor c = mDatabaseHelper.query(MyDatabaseHelper.TABLE_USERS, MyDatabaseHelper.COL_NAME);
    String[] from = new String[]{MyDatabaseHelper.COL_NAME, MyDatabaseHelper.COL_EMAIL};
    int[] to = { android.R.id.text1, android.R.id.text2 };
 
    SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, android.R.layout.simple_list_item_2, c, from, to, 0);
 
    ListView listView = (ListView) findViewById(R.id.listView);
    listView.setAdapter(adapter);
 
    // ...
  }

Finally, you’ll see that we’ve called a new method, query(), on our database helper. The parameters specify the table to query, and how to sort results. This will provide a simple way to retrieve the records from our database, wrapping the SQLiteOpenHelper‘s own query() method. Add the new method to MyDatabaseHelper:

 // src/com/example/MyDatabaseHelper.java
  public Cursor query(String tableName, String orderedBy) {
    String[] projection = {COL_ID, COL_NAME, COL_EMAIL, COL_DOB};
    return getReadableDatabase().query(tableName, projection, null, null, null, null, orderedBy);
  }

Of course, we could add other parameters such as search terms to our query(), making it very powerful – and again providing a single point of contact for querying our database.

Running the code, you’ll now see the list of names and email addresses as people are added to the database. Note that every time you re-run the app, another set of records will be added as the calls to addUser() are in the activity’s onCreate() method.

Screenshot of App Running

As you can see, it’s relatively easy to build a powerful abstraction layer between your database and application code, and there are huge advantages in doing so. As your app grows, you’ll start to build a library of common methods that will save you time and make your code more robust.

Coming Next

In the next tutorial, we’ll add another table to our database and adapt our database helper class to handle multiple tables and joins. In the final part, we’ll look at Android’s ContentProvider API, which provide a standard way to abstracting and manage access to your your app’s raw database.

Thanks for reading! If you’ve liked this tutorial, please let me know by leaving your comments and feedback on Twitter or Google+.

Join the free Plymouth Software newsletter today to get news, tutorials, hints and tips delivered straight to your inbox.

Testing Rendered Views with RSpec2, Capybara and Rails 3.

While view tests are brittle, easily breaking when the design of a page changes, they are undoubtedly handy for checking the important parts of a page are rendered. I usually check for page titles, model attributes/tables or forms, and footer element, and so on.

Capybara is a great DSL for testing a page’s rendered output, but unfortunately the Capybara matchers are not available in view or helper specs according to the rspec-rails documentation.

Thanks to this answer on Stack Overflow, it’s possible to load a page’s rendered content into a controller spec, and test it using the Capybara matchers.

I’m posting some example code here based on the answer for future reference, and to help anyone struggling to use Capybara with Rspec 2:

# Gemfile
group :test do
  gem 'capybara'
  gem 'rspec-rails'
end
# spec/controllers/users_controller_spec.rb
require 'spec_helper'
 
describe UsersController do
  # Render views and load the rendered content into Capybara for matching
  # See: http://stackoverflow.com/questions/4706370/rspec-view-testing-with-capybara-and-rails3/4773050#4773050#
  render_views
  let(:page) { Capybara::Node::Simple.new(@response.body) }
 
  describe "GET 'index'" do
    before { get :index }
 
    it { should respond_with(:success) }
    it { should assign_to(:users).with_kind_of(Array) }
    it { should render_template(:index) }
 
    # Test the rendered view using Capybara matchers
    it { page.should have_selector("h2") }
    it { page.should have_content("All Users") }
    it { page.should have_selector("table#users") }
    # ... add more tests ...
    it { page.should have_selector("div#footer" }
  end
end

Helpful post? Let me know by commenting on Twitter.

Android: How to test Local Web Apps on an Emulated Device (AVD)

Android AVD running local Web App

Editing your /etc/hosts file is great for running and testing web apps as they are under development. For a project I’m working on, I needed to be able to access one of my local Rails apps through an Android device, which meant editing the device’s /etc/hosts file. As I haven’t rooted my Nexus-S, so opted to use an emulated Android Virtual Device (AVD), as they are configured by default to allow root access.

I started by creating a simple hosts file on my desktop. It’s this file that I would push to the AVD

cd Desktop
vim hosts # or your favourite text editor....

Use your text editor to create a simple hosts file pointing to your local web app. Remember to also include a localhost entry:

  # hosts
  127.0.0.1       localhost 
  192.168.1.10    rails-app

Next, copy your new hosts file to the AVD:

adb push hosts /etc/hosts

Access Denied

Despite having root access, though, adb wouldn’t let me push the new hosts file because the system partition is mounted as read-only. You can remount the system partition in read-write mode using adb remount, and then try pushing the hosts file up to the device:

adb remount
abd push hosts /etc/hosts

I kept getting an Out of Memory error, which seems to be a common problem. The solution (gleaned from scattered forum posts) is to use the emulator’s -partition-size option.

Using this option means you won’t be able to launch the AVD directly from Eclipse, but instead need to use the command line. Close down any running AVDs, and then run, and then re-launch the AVD passing in the -partition-size option with a reasonable value:

emulator -avd Samsung_Galaxy-Tab -partition-size 128
 
# ...wait the avd to boot up...
 
adb remount
abd push hosts /etc/hosts

The hosts file should now successfully be copied to your AVD. If you launch the AVD’s browser and enter the local URL from your hosts file (e.g. http://rails-app:3000/), the AVD will connect to the local IP address you specified.

Note: You’ll need to keep hold of your hosts file as your settings will be wiped when you shut down the AVD. You can reinstall the file next time by performing the adb remount; adb push hosts /etc/hosts script each time you boot the AVD.

References

  1. Google Groups Thread
  2. Cute Android Tips: Failed to Copy File to System

Rails 3: How to Autoload and Autorequire your Custom Library Code

Every time I start a new Rails 3 project, I’m always caught out by its autoloading behaviour. Rails 3 will only require (and so autoload) a module when it is first encountered within the application code, for example by a call to include or require.

Whilst the reasoning behind this decision is sound, I usually just want to load some common functionality into, for example ActiveRecord, and have it available to all of my models.

So, as a reminder to myself and to help anyone else caught out by this, here is how to autoload (and autorequire) your own library code in Rails 3:

Ensure that your library code’s path is set in config/application.rb. By default, Rails 3 autoloads from a /extras folder, but I conventionally keep custom library code in a /lib folder:

# config/application.rb
config.autoload_paths += %W(#{config.root}/extras #{config.root}/lib)

Next, we need to tell Rails to require our library code (so it is available to the application). Create a new initializer called application.rb in config/initializers and require your library modules:

# config/initializers/application.rb
require 'my_modules'
require 'my_modules/active_record/active_record_extensions'
# ... require any other custom modules your application uses

All that’s left to do is restart your Rails server to load your custom modules into the application. Remember that if you change your custom code, you’ll need to restart the server again to reload the changes.

Ubuntu 11.04 Beta – How to test Unity in VirtualBox 4

Ubuntu 11.04 Beta ScreenshotIf you like to test out the latest and greatest builds of Ubuntu, you might like to try the latest 11.04 beta in VirtualBox.

Unfortunately, I’ve found the standard VirtualBox guest additions don’t offer the graphics support to run compiz on Ubuntu 11.04, so testing Unity didn’t seem possible. Thankfully, I stumbled across this post (Portugese) which shows you can install guest additions directly from the Ubuntu repositories:

With a freshly installed copy of Ubuntu 11.04 Beta running in VirtualBox, open a terminal window and type the following:

sudo apt-get update; sudo apt-get install virtualbox-ose-guest-utils -y; sudo reboot

Update: Also make sure you’ve enabled 3D acceleration in the guest machine’s settings.

This line will install the up-to-date VirtualBox guest utilities, and reboot the virtual machine. You should now find the new – and very impressive – Unity interface ready to try out.

References

Ubuntu 11.04 in VirtualBox with Natty Narwhal Unity