RoomDatabase onCreate两次调用

时间:2018-06-29 19:46:56

标签: android kotlin android-room

调用RoomDatabase.Callback时,我正在使用onCreate填充数据库。根据文档,此方法仅在首次创建数据库时才被称为一次

  

在首次创建数据库时调用。这就是所谓的   在创建所有表之后。

但是由于某种原因,它被两次调用(有时超过两次)。如果在第二次调用中替换了数据,这不会有太大的问题,但是由于每个调用都使用单独的输入调用启动了一个新线程,因此会创建重复的数据。

这是有问题的数据库。

@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)
        }

    }

}

为了添加FTS支持,我正在做一些奇怪的事情,但这不应导致onCreate()被调用两次;特别是考虑到我在另一个数据库中执行相同的操作,不会导致相同的问题。

@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)
        }

    }

}

我在单独的存储库类中将回调添加到数据库中。

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

}

我不知道为什么会这样,尤其是考虑到它仅发生在我的两个(非常相似)实现中的一个。

2 个答案:

答案 0 :(得分:1)

class DirectoryRepository可能会多次实例化,并且每次init调用都会添加回调。


除此之外,您还应该添加addCallback()类的生成器提供的RoomDatabase回调。

否则,您可能会面临相反的问题,即根本不会触发回调。如果您在通过SupportSQLiteOpenHelper方法创建的<database-class>_Impl.createOpenHelper(...)之后手动添加了回调,则会发生这种情况。

答案 1 :(得分:0)

即使在同步块中,您也应检查instance == null

    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
        }
    }