Android Native - How to prepopulate a Room database

dimitrilc 1 Tallied Votes 1K Views Share

Introduction

If your Android Application uses Room, then you might have wondered how to prepopulate a Room database. In this tutorial, we will learn how to prepopulate a Room database.

Goals

At the end of the tutorial, you would have learned:

  1. How to prepopulate a Room database.

Tools Required

  1. Android Studio. The version used in this tutorial is Bumblebee 2021.1.1 Patch 2.

Prerequisite Knowledge

  1. Basic Android.
  2. Basic Room.

Project Setup

To follow along with the tutorial, perform the steps below:

  1. Create a new Android project with the default Empty Activity.

  2. Add the dependencies below for Room into the Module build.gradle.

     def room_version = "2.4.2"
     implementation "androidx.room:room-runtime:$room_version"
     kapt "androidx.room:room-compiler:$room_version"
     implementation "androidx.room:room-ktx:$room_version"
     implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.1'
  3. In the same file, add the kapt plugin under plugins.

     id 'kotlin-kapt'
  4. Add a new Person Entity with the code below.

     @Entity(tableName = "person")
     data class Person(
        @PrimaryKey(autoGenerate = true) val id: Long,
        @ColumnInfo(name = "first_name") val firstName: String,
        @ColumnInfo(name = "last_name") val lastName: String
     )
  5. Add a new Person DAO with the code below.

     @Dao
     interface PersonDao {
        @Query("SELECT * FROM person WHERE id=:id")
        suspend fun getPersonById(id: Long): Person?
     }
  6. Create a new Room database with the code below.

     @Database(entities = [Person::class], version = 1)
     abstract class MyDatabase : RoomDatabase(){
        abstract fun personDao(): PersonDao
     }
  7. In MainActivity#onCreate(), append the code below. This creates a dummy connection to the database at launch so that we can have access to it in debug mode.

     val db = Room.databaseBuilder(
        applicationContext,
        MyDatabase::class.java, "my-database"
     ).build()
    
     lifecycleScope.launch {
        db.personDao().getPersonById(1)
     }

Project Overview

For this tutorial, we are not concerned about the UI at all and only focus on the Room database itself.

There is only one simple Student Entity in our App. At the end of the tutorial, we should be able to prepopulate our database with data on boot.

Options for prepopulating data

There are three different ways to prepopulate a database.

  1. The first option involves using prepackaged database files located under a special directory called assets/.
  2. The second option allows you to load the prepackaged files from the file system itself.
  3. The third option includes loading the database files from an InputStream.

Because loading pre-packaged databases are all the same conceptually for all three options, we will only need to know how to do it with the assets/ resource directory. Loading a pre-packaged database from File or InputStream is only different depending on the location of the file.

All options require you to export your pre-built database, so we will learn how to do that first.

Export an Existing Database

To be able to export a database, we will need to build it first on a test device. Follow the steps below to create some data and export the database.

  1. Select the top-most app directory of in the Android Project View.

  2. File > New > Folder > Assets Folder.

  3. Start the App in Debug mode.

  4. Open the App Inspection tab to start the Database Inspector.

  5. Run the statement below to add some Person into the database.

     INSERT INTO person VALUES
     (1, "Terence", "Landon"),
     (2, "Danielle", "Alger"),
     (3, "Arnold", "Chandler"),
     (4, "Mariah", "Blake"),
     (5, "Randal", " Maria ")
  6. Now, highlight the my-database and select Export as File. Save it as a .db file.
    5.png

  7. You can export directly into the assets directory created previously.

  8. Optionally, save another copy of the same database as the SQL file extension, and then open the file with a text editor. With the human-readable SQL file, we now understand exactly which statements are run against the underlying SQLite database.

     PRAGMA foreign_keys=OFF;
     BEGIN TRANSACTION;
     CREATE TABLE android_metadata (locale TEXT);
     INSERT INTO android_metadata VALUES('en_US');
     CREATE TABLE `person` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `first_name` TEXT NOT NULL, `last_name` TEXT NOT NULL);
     INSERT INTO person VALUES(1,'Terence','Landon');
     INSERT INTO person VALUES(2,'Danielle','Alger');
     INSERT INTO person VALUES(3,'Arnold','Chandler');
     INSERT INTO person VALUES(4,'Mariah','Blake');
     INSERT INTO person VALUES(5,'Randal',' Maria ');
     CREATE TABLE room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT);
     INSERT INTO room_master_table VALUES(42,'cd4ee16aca420ae15eab14b1baa5cdcf');
     DELETE FROM sqlite_sequence;
     INSERT INTO sqlite_sequence VALUES('person',5);
     COMMIT;

Load prepackaged database from app Asset

Now that we have the exported database, it is quite simple to load it into our database on the first run. All we have to do is to call the function createFromAsset() from the Room.Builder class on our database creation call. The builder step to add is:

.createFromAsset("my-database.db")

so that the previous database builder becomes this:

val db = Room.databaseBuilder(
   applicationContext,
   MyDatabase::class.java,
   "my-database"
)
   .createFromAsset("my-database.db")
   .build()

If you reinstall the app, then you can see that the database now has data upon first boot.

6.png

Solution Code

MainActivity.kt

package com.codelab.daniwebandroidprepopulateroomdatabase

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.lifecycle.lifecycleScope
import androidx.room.Room
import kotlinx.coroutines.launch

class MainActivity : AppCompatActivity() {
   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)
       setContentView(R.layout.activity_main)

       val db = Room.databaseBuilder(
           applicationContext,
           MyDatabase::class.java,
           "my-database"
       )
           .createFromAsset("my-database.db")
           .build()

       lifecycleScope.launch {
           db.personDao().getPersonById(1)
       }
   }
}

MyDatabase.kt

package com.codelab.daniwebandroidprepopulateroomdatabase

import androidx.room.Database
import androidx.room.RoomDatabase

@Database(entities = [Person::class], version = 1)
abstract class MyDatabase : RoomDatabase(){
   abstract fun personDao(): PersonDao
}

Person.kt

package com.codelab.daniwebandroidprepopulateroomdatabase

import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity(tableName = "person")
data class Person(
   @PrimaryKey(autoGenerate = true) val id: Long,
   @ColumnInfo(name = "first_name") val firstName: String,
   @ColumnInfo(name = "last_name") val lastName: String
)

PersonDao

package com.codelab.daniwebandroidprepopulateroomdatabase

import androidx.room.Dao
import androidx.room.Query

@Dao
interface PersonDao {
   @Query("SELECT * FROM person WHERE id=:id")
   suspend fun getPersonById(id: Long): Person?
}

Module build.gradle

plugins {
   id 'com.android.application'
   id 'org.jetbrains.kotlin.android'
   id 'kotlin-kapt'
}

android {
   compileSdk 32

   defaultConfig {
       applicationId "com.codelab.daniwebandroidprepopulateroomdatabase"
       minSdk 21
       targetSdk 32
       versionCode 1
       versionName "1.0"

       testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
   }

   buildTypes {
       release {
           minifyEnabled false
           proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
       }
   }
   compileOptions {
       sourceCompatibility JavaVersion.VERSION_1_8
       targetCompatibility JavaVersion.VERSION_1_8
   }
   kotlinOptions {
       jvmTarget = '1.8'
   }
}

dependencies {
   def room_version = "2.4.2"
   implementation "androidx.room:room-runtime:$room_version"
   kapt "androidx.room:room-compiler:$room_version"
   implementation "androidx.room:room-ktx:$room_version"
   implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.1'

   implementation 'androidx.core:core-ktx:1.7.0'
   implementation 'androidx.appcompat:appcompat:1.4.1'
   implementation 'com.google.android.material:material:1.5.0'
   implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
   testImplementation 'junit:junit:4.13.2'
   androidTestImplementation 'androidx.test.ext:junit:1.1.3'
   androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}

Summary

We have learned how to prepopulate a Room database in this tutorial. The full project code can be found at https://github.com/dmitrilc/DaniwebAndroidPrepopulateRoomDatabase

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.