代码之家  ›  专栏  ›  技术社区  ›  Bryan

RoomDatabase OnCreate调用了两次

  •  0
  • Bryan  · 技术社区  · 8 年前

    我用的是 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 回复  |  直到 8 年前
        1
  •  1
  •   Maxim    8 年前

    有可能 class DirectoryRepository 多次实例化,并在每个 init 调用。


    除此之外,还应添加回调 addCallback() 由建筑商提供 RoomDatabase 类。

    否则,您可能会面临一个相反的问题,即回调根本不会被触发。如果在 SupportSQLiteOpenHelper 创建于 <database-class>_Impl.createOpenHelper(...) 方法。

        2
  •  0
  •   EpicPandaForce Jigar Joshi    8 年前

    你应该检查一下 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
            }
        }