I made a screen like the current image.
Data such as A, B, C..
are currently being set by getting from the strings.xml
resource file.
I am now going to use Room DB
instead of strings.xml
and I want to get these data from Room
.
To do this, we need to pre-populate the Room
with data.
In the sample code I found, the method called addCallback()
was usually used.
like this :
@Database(entities = arrayOf(Data::class), version = 1)
abstract class DataDatabase : RoomDatabase() {
abstract fun dataDao(): DataDao
companion object {
@Volatile private var INSTANCE: DataDatabase? = null
fun getInstance(context: Context): DataDatabase =
INSTANCE ?: synchronized(this) {
INSTANCE ?: buildDatabase(context).also { INSTANCE = it }
}
private fun buildDatabase(context: Context) =
Room.databaseBuilder(context.applicationContext,
DataDatabase::class.java, "Sample.db")
// prepopulate the database after onCreate was called
.addCallback(object : Callback() {
override fun onCreate(db: SupportSQLiteDatabase) {
super.onCreate(db)
// insert the data on the IO Thread
ioThread {
getInstance(context).dataDao().insertData(PREPOPULATE_DATA)
}
}
})
.build()
val PREPOPULATE_DATA = listOf(Data("1", "val"), Data("2", "val 2"))
}
}
However, as you can see from the code, in the end, data (here, val PREPOPULATE_DATA
) is being created again within the code. (In another code, db.execSQL()
is used)
In this way, there is no difference from fetching data from resource file in the end.
Is there any good way?
Developer documentation uses assets
and files
.
However, it is said that it is not supported within In-memory Room databases
.
In this case, I do not know what In-memory
means, so I am not using it.
In this case, I do not know what In-memory means, so I am not using it.
In-Memory will be a database that is not persistent, that is the database is created using in memory rather than as a file, at some time it will be deleted. You probably do not want an in-memory database.
However, as you can see from the code, in the end, data (here, val PREPOPULATE_DATA) is being created again within the code. (In another code, db.execSQL() is used)
This is a common misconception when writing Apps as the onCreate
method of an activity is often repeated when an App is running. With an SQLite database the database is created once in it's lifetime, which would be from the very first time the App is run until the database file is deleted. The database will otherwise remain (even between App version changes).
Is there any good way?
You basically have two options for a pre-populated database. They are
Option 1 -Adding data
If the data should only be added once then using the overridden onCreate
method via the CallBack
can be used. However, using functions/methods from the @Dao
annotated class(es) should not be used. Instead only SupportSQLiteDatabase functions/methods should be used e.g. execSQL
(hence why the SupportSQLiteDatabase is passed to onCreate).
This is because at that stage the database has just been created and all the underlying processing has not been completed.
You could protect against duplicating data quite easily by using INSERT OR IGNORE ....
rather than INSERT ....
. This will skip insertion if there is an applicable constraint violation (rule being broken). As such it relies upon such rules being in force.
The two most commonly used constraints are NOT NULL
and UNIQUE
, the latter implicitly for a primary key.
autogenerate
.This option could be suitable for a small amount of data but if over used can start to bloat the code and result in it's maintainability being compromised.
INSERT or IGNORE
were utilised (or alternative checks) then this could, at some additional overhead, even be undertaken in the Callback's onOpen
method. This being called every time the database is opened.Pre-packaged Database
If you have lots of initial data, then creating the database externally, including it as an asset (so it is part of the package that is deployed) and then using Room's .createFromAsset (or the rarer used .createFromFile
) would be the way to go.
However, the downfall with this, is that Room expects such a database to comply with the schema that it determines and those expectations are very strict. As such just putting together a database without understanding the nuances of Room then it can be a nightmare.
However, the easy way around this is to let Room tell you what the schema it expects is. To do so you create the @Entity annotated classes (the tables), create the @Database annotated class, including the respective entities in the entities parameter and then compile. In Android Studio's Android View java(generated) will then be visible in the explorer. Within that there will be a class that is the same name as the @Database annotated class but suffixed with _Impl. Within this class there is a function/method createAllTables
and it includes execSQL
statements for all the tables (the room_master_table should be ignored as Room will always create that itself).
The database, once created and saved, should be copied into the assets folder and using .createFromAsset(????) will then result in the pre-packaged data being from the package to the appropriate local storage location.