Reputation: 15135
I am using the RoomDatabase.Callback
to populate the database when onCreate
is called. According to the documentation, this method should only be called once when the database is first created.
Called when the database is created for the first time. This is called after all the tables are created.
But for some reason it is being called twice (sometimes more than twice). This wouldn't be too much of an issue it the data was being replaced on the second call, but because each call starts a new thread with separate input calls it is creating duplicate data.
This is the database in question.
@Database(entities = [FTSPlaceholder::class], version = 1)
abstract class DirectoryDatabase : RoomDatabase() {
companion object {
const val NAME = "directory_database"
@Volatile private var INSTANCE: DirectoryDatabase? = null
fun getInstance(context: Context): DirectoryDatabase = INSTANCE ?: synchronized(this) {
INSTANCE ?: buildDatabase(context).also { INSTANCE = it }
}
private fun buildDatabase(context: Context): DirectoryDatabase =
Room.databaseBuilder(context.applicationContext, DirectoryDatabase::class.java, NAME)
.addCallback(FTSCallback()).build()
}
fun addCallback(callback: Callback) {
if (mCallbacks == null) mCallbacks = ArrayList()
mCallbacks?.add(callback)
}
abstract fun departmentDao(): DepartmentDao
abstract fun employeeDao(): EmployeeDao
private class FTSCallback : Callback() {
companion object {
private const val CREATE_TABLE_DEPARTMENTS =
"CREATE VIRTUAL TABLE IF NOT EXISTS `departments` " +
"USING FTS4(`code` TEXT NOT NULL, `title` TEXT NOT NULL, " +
"`location` TEXT NOT NULL, `phone` TEXT, `fax` TEXT, PRIMARY KEY(`code`))"
private const val CREATE_TABLE_PERSONNEL =
"CREATE VIRTUAL TABLE IF NOT EXISTS `personnel` " +
"USING FTS4(`familyName` TEXT NOT NULL, `givenName` TEXT NOT NULL, " +
"`middleName` TEXT, `title` TEXT NOT NULL, `location` TEXT NOT NULL, " +
"`room` TEXT NOT NULL, `phone1` TEXT NOT NULL, `phone2` TEXT NOT NULL, " +
"`email` TEXT NOT NULL, `fax` TEXT, `department` TEXT NOT NULL, " +
"`school` TEXT, PRIMARY KEY(`familyName`, `givenName`, `title`))"
}
override fun onCreate(db: SupportSQLiteDatabase) {
db.execSQL(CREATE_TABLE_DEPARTMENTS)
db.execSQL(CREATE_TABLE_PERSONNEL)
}
}
}
I am doing some strange things in order to add FTS support, but that shouldn't cause onCreate()
to be called twice; especially considering I do the same thing in another database, which doesn't cause the same issue.
@Database(entities = [Area::class], version = 1)
abstract class MapDatabase : RoomDatabase() {
companion object {
const val NAME = "map_database"
@Volatile private var INSTANCE: MapDatabase? = null
fun getInstance(context: Context): MapDatabase = INSTANCE ?: synchronized(this) {
INSTANCE ?: buildDatabase(context).also { INSTANCE = it }
}
private fun buildDatabase(context: Context): MapDatabase =
Room.databaseBuilder(context.applicationContext, MapDatabase::class.java, NAME)
.addCallback(FTSCallback()).build()
}
fun addCallback(callback: Callback) {
if (mCallbacks == null) mCallbacks = ArrayList()
mCallbacks?.add(callback)
}
abstract fun placeDao(): PlaceDao
abstract fun areaDao(): AreaDao
private class FTSCallback : Callback() {
companion object {
private const val CREATE_TABLE_PLACES =
"CREATE VIRTUAL TABLE IF NOT EXISTS `places` " +
"USING FTS4(`id` INTEGER NOT NULL, `title` TEXT NOT NULL, " +
"`subtitle` TEXT, `description` TEXT, `latitude` REAL NOT NULL, " +
"`longitude` REAL NOT NULL, `type` INTEGER NOT NULL, " +
"`parent` INTEGER, PRIMARY KEY(`id`))"
}
override fun onCreate(db: SupportSQLiteDatabase) {
db.execSQL(CREATE_TABLE_PLACES)
}
}
}
I add the callback to the database in a separate repository class.
class DirectoryRepository(application: Application) {
private val database = DirectoryDatabase.getInstance(application)
init {
database.addCallback(object : RoomDatabase.Callback() {
// This method is being called twice
override fun onCreate(db: SupportSQLiteDatabase) {
refresh()
}
}
}
// Code omitted for brevity
}
I cannot figure out why this would be the case, especially considering it only happens to one of my two (very similar) implementations.
Upvotes: 0
Views: 700
Reputation: 1254
There is a chance that theclass DirectoryRepository
instantiated more than once and the callback added on each init
invocation.
Besides that, you should add callbacks with addCallback()
provided by the builder of the RoomDatabase
class.
Otherwise, you might face with an opposite issue, that a callback won't fired at all. This can happen if you manually add a callback after SupportSQLiteOpenHelper
created in <database-class>_Impl.createOpenHelper(...)
method.
Upvotes: 1
Reputation: 81539
You should check for instance == null
even in the synchronized block.
fun getInstance(context: Context): DirectoryDatabase {
return INSTANCE ?: synchronized(this) {
if(INSTANCE != null) {
return@synchronized database
}
val database = Room.databaseBuilder(context.applicationContext,
DirectoryDatabase::class.java, NAME)
.addCallback(FTSCallback())
.build()
INSTANCE = database
return@synchronized database
}
}
Upvotes: 0