Предварительное заполнение базы данных Room с помощью Hilt без создания дополнительного экземпляра базы данных

Я пытаюсь убедиться, что моя база данных всегда содержит начальную строку. Я прочитал Как сначала заполнить таблицу базы данных Android Room run? и главное, с чем я сталкиваюсь, это то, что у меня нет экземпляра для доступа (или я не знаю, как получить к нему доступ?) с помощью Hilt при создании базы данных. Если я попытаюсь повторно использовать написанный мной метод provideDatabase Hilt, это приведет к утечке базы данных SQLite (предположительно, потому, что рядом нет никого, кто мог бы закрыть базу данных с помощью этих порожденных экземпляров). Вот мой код:

@Module
@InstallIn(ApplicationComponent::class)
object AppModule {

    @Singleton
    @Provides
    fun provideDatabase(@ApplicationContext context: Context): GameDatabase {
        return Room.databaseBuilder(context, GameDatabase::class.java, GameDatabase.GAME_DB_NAME)
            .addCallback(
                object : RoomDatabase.Callback() {
                    override fun onCreate(db: SupportSQLiteDatabase) {
                        super.onCreate(db)
                        // Initialize the database with the first game
                        ioThread {
                            provideDatabase(context).gameDao().createNewGame(Game())
                        }
                    }

                    override fun onOpen(db: SupportSQLiteDatabase) {
                        super.onOpen(db) 

                        // Ensure there is always one game in the database
                        // This will capture the case of the app storage
                        // being cleared
                        // THIS results in an instance being created that can't be closed - causing DB leaks!
                        ioThread {
                            val gameDao = provideDatabase(context).gameDao()

                            if (gameDao.gameCount() == 0) {
                                gameDao.createNewGame(Game())
                            }
                        }
                    }
                }
            ).build()
    }

    @Singleton
    @Provides
    fun provideGameDao(database: GameDatabase): GameDao {
        return database.gameDao()
    }
}

Итак, как мне получить доступ к ДАО для инициализации? Нужно ли мне просто вручную создавать оператор вставки в SQL и вызывать его в базе данных?


person Chantell Osejo    schedule 20.01.2021    source источник


Ответы (2)


Ваш метод provideDatabase всегда создает новый экземпляр всякий раз, когда он вызывается: Dagger делает его одноэлементным, вызывая этот метод только один раз. Единственный способ получить одноэлементный экземпляр GameDatabase, управляемый ApplicationComponent, — это запросить его как зависимость. Поскольку GameDatabase должен будет зависеть сам от себя через GameDao, это циклическая зависимость.

Чтобы разрешить циклическую зависимость в Dagger, вы можете использовать Provider или Lazy:

    @Singleton
    @Provides
    fun provideDatabase(@ApplicationContext context: Context, gameDaoProvider: Provider<GameDao>): GameDatabase {
        return Room.databaseBuilder(context, GameDatabase::class.java, GameDatabase.GAME_DB_NAME)
            .addCallback(
                object : RoomDatabase.Callback() {
                    override fun onCreate(db: SupportSQLiteDatabase) {
                        super.onCreate(db)
                        // Initialize the database with the first game
                        ioThread {
                            gameDaoProvider.get().createNewGame(Game())
                        }
                    }

                    override fun onOpen(db: SupportSQLiteDatabase) {
                        super.onOpen(db) 

                        // Ensure there is always one game in the database
                        // This will capture the case of the app storage
                        // being cleared
                        // This uses the existing instance, so the DB won't leak
                        ioThread {
                            val gameDao = gameDaoProvider.get()

                            if (gameDao.gameCount() == 0) {
                                gameDao.createNewGame(Game())
                            }
                        }
                    }
                }
            ).build()
    }
person Nitrodon    schedule 20.01.2021
comment
Спасибо, это сработало как шарм. Больше никаких уродливых предупреждений SQLiteDatabase об утечках памяти! Оцените быструю помощь. - person Chantell Osejo; 21.01.2021

Есть одна проблема:

@Singleton
@Provides
fun provideGameDao(database: GameDatabase): GameDao {
    return database.gameDao()
}

должно быть:

@Provides
fun provideGameDao(database: GameDatabase): GameDao {
    return database.gameDao()
}
person Martin Zeitler    schedule 20.01.2021