Room Persistence Library With Pre-Populated Database

Room persistence library which is released as a part of Android Architecture Components provides an abstraction layer for SQLite databases. Room has made SQLite database management easier for Android developers and we can use powerful components such as LiveData thanks to it.

Normally Room databases are expected to be created during app runtime when needed. The official documentation and the tutorials on Room are based on that expectation. What about if you already have your own pre-populated database file and want to deploy it at the first launch of your app? Think of a dictionary app where you have a database file including all the words with their translations and you want to use this database file with Room.

Room Architecture Diagram (credit)

Before Room, the way you handle this would be probably placing your database file in app\src\main\assets folder and make your app copy this database file into app data folder at runtime. Then you can do your regular SQL operations on the copied database.

The good news is this principle will be valid for Room with some additions. Following sections will argue the whole process for using your offline or pre-populated database with Room.

Copy Database

I will not dive into the details of Room since this post assumes you are already familiar with it. If not I recommend you to take a look at the official documentation before continuing with the rest of this post.

First you need to place your database file in app\src\main\assets folder within your project folder.

Below code is to copy this database file from assets folder to app’s data directory.

final InputStream inputStream = context.getAssets().open("databases/" + databaseName);
final OutputStream output = new FileOutputStream(dbPath);

byte[] buffer = new byte[8192];
int length;

while ((length = inputStream.read(buffer, 0, 8192)) > 0) {
output.write(buffer, 0, length);
}

The whole class used for copying the database is given below. You should change DATABASE_NAME string with your database file name.

Looking at DatabaseCopier class we see two main operations:

copyAttachedDatabase(appContext, DATABASE_NAME);
mAppDataBase = Room.databaseBuilder(appContext,
AppDatabase.class, DATABASE_NAME)
.addMigrations(AppDatabase.MIGRATION_1_2)
.build();

copyAttachedDatabase call is to physically copying the database file from assets folder.

The second call including Room.databaseBuilder(..) is to create the Room database and we will look at this call in detail in the following section.

Room Migration Adjustments

The database file is copied from assets folder to app data folder and you now have your SQLite database included in your project. To use a regular SQLite database as Room database, a migration operation is needed. Here you should tell how this SQLite database will be migrated to Room database. We tell Room how to do migrations by:

addMigrations(AppDatabase.MIGRATION_1_2)

call which dictates the migration rules. Below is the AppDatabase class which contains migration rules. For this case Room is told a migration of database from version 1 to version 2 should happen.

@Database(entities = [(Word::class)], version = 2)
abstract class AppDatabase : RoomDatabase() {
abstract fun getWordDao(): WordDao

companion object {
@JvmField
val MIGRATION_1_2 : Migration = object : Migration(1, 2) {
override fun migrate(database: SupportSQLiteDatabase) {
}
}

}
}

Word is our Entity class and declared as below. Its declaration is up to you except the fields have to be exactly same with your database. In other words the field names and field features have to be same as the declarations in database file. Otherwise Room will throw an exception indicating the database scheme and declaration are incompatible.

@Entity(tableName = "wordlist")
data class Word(
@PrimaryKey(autoGenerate = true)
var wordId: Int? = null,
var wordfrom: String,
var wordto: String,
var favflag: String)

The database wordlist table for this post is declared as below. Note that wordId is nullable in table and it is declared in Word.kt as nullable too. The other fields cannot be null as stated in database table and Word.kt has them not nullable.

CREATE TABLE `wordlist` (
`wordId` INTEGER,
`wordfrom` TEXT NOT NULL,
`wordto` TEXT NOT NULL,
`favflag` TEXT NOT NULL,
PRIMARY KEY(`wordId`)
);

Done

That is it. We have first copied database from assets folder and then told Room to migrate from it. When doing the migration call we make sure the database table properties conform to our Entity class for Room.

You have now your Room database created with a pre-populated database and you should be using all Room features as a charm. Happy coding.

Android Developer