简体   繁体   English

RoomDatabase onCreate调用了两次

[英]RoomDatabase onCreate called twice

I am using the RoomDatabase.Callback to populate the database when onCreate is called. 调用onCreate时,我正在使用RoomDatabase.Callback填充数据库。 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; 为了增加对FTS的支持,我在做一些奇怪的事情,但这不应该导致onCreate()被调用两次。 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. 我无法弄清楚为什么会这样,尤其是考虑到它仅发生在我的两个(非常相似)实现中之一。

There is a chance that the class DirectoryRepository instantiated more than once and the callback added on each init invocation. class DirectoryRepository有可能多次实例化,并且在每次init调用时都会添加回调。


Besides that, you should add callbacks with addCallback() provided by the builder of the RoomDatabase class. 除此之外,您应该使用RoomDatabase类的构建器提供的addCallback()添加回调。

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. 如果在<database-class>_Impl.createOpenHelper(...)方法中创建的SupportSQLiteOpenHelper之后手动添加回调,则会发生这种情况。

You should check for instance == null even in the synchronized block. 即使在同步块中,也应检查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
        }
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM