如何使用 Dagger Hilt 和 Room 将数据库范围限定到用户?
How to scope a Database to a User using Dagger Hilt and Room?
我正在尝试根据他们登录的用户 ID 提供不同的数据库,这样如果您使用不同的帐户登录,每个用户都可以拥有自己的数据库。我不想提供“所有者”字段,我试过了但不喜欢。
现在的问题是,数据库的范围限定在应用程序范围内,因此一旦用户注销并且另一个用户登录,数据库就不会重新创建。只有在重新启动应用程序时,才会创建正确的数据库。
如何使用 Hilt 实现此类行为?
这是现在的样子:
@InstallIn(SingletonComponent::class)
@Module
object ModuleDatabase {
@Provides
@Singleton
fun provideAppDatabase(
@ApplicationContext context: Context,
userManager: UserManager,
): MyRoomDatabase {
return Room.databaseBuilder(context, MyRoomDatabase::class.java, LOCAL_DATABASE_NAME + "_${userManager.loggedInUid()}")
.addMigrations(...)
.fallbackToDestructiveMigration()
.build()
}
}
原来我需要创建一个符合要求的自定义Hilt组件:
- https://dagger.dev/hilt/custom-components.html
- https://medium.com/androiddevelopers/hilt-adding-components-to-the-hierarchy-96f207d6d92d
(请记住,这不适用于 WorkManager)
更新: 它实际上是通过避免 Hilt Singleton 作用域并自己构建它来工作的。这也适用于 WorkManager,因为 WorkManager 可以访问 SingletonComponents 内部的依赖项。
它基本上是这样工作的:
@InstallIn(SingletonComponent::class)
@Module
object ModuleDatabase {
private var databaseName: String by Delegates.observable(LOCAL_DATABASE_NAME) { _, oldValue, newValue ->
if (oldValue != newValue) {
databaseConnection?.let {
if (it.isOpen) {
it.close()
}
databaseConnection = null
// new connection opens with new name
}
}
}
private var databaseConnection: TemporyRoomDatabase? = null
private fun buildDatabase(context: Context, name: String): TemporyRoomDatabase {
Timber.e("Building database $databaseName")
return Room.databaseBuilder(context, TemporyRoomDatabase::class.java, name)
.addMigrations(*TemporyRoomDatabase_Migrations.build())
.fallbackToDestructiveMigration()
.build()
}
private fun getDatabaseConnection(context: Context, newDatabaseName: String): TemporyRoomDatabase {
return if (databaseName != newDatabaseName) {
databaseName = newDatabaseName
buildDatabase(context, databaseName).also {
databaseConnection = it
}
} else {
// use existing connection
Timber.e("Reusing database $databaseName")
databaseConnection ?: buildDatabase(context, databaseName)
}
}
@Provides
fun provideAppDatabase(
@ApplicationContext context: Context,
userManager: UserManager,
): TemporyRoomDatabase {
return getDatabaseConnection(context, LOCAL_DATABASE_NAME + "_${userManager.loggedInUidOrNull()}")
}
}
我正在尝试根据他们登录的用户 ID 提供不同的数据库,这样如果您使用不同的帐户登录,每个用户都可以拥有自己的数据库。我不想提供“所有者”字段,我试过了但不喜欢。
现在的问题是,数据库的范围限定在应用程序范围内,因此一旦用户注销并且另一个用户登录,数据库就不会重新创建。只有在重新启动应用程序时,才会创建正确的数据库。
如何使用 Hilt 实现此类行为?
这是现在的样子:
@InstallIn(SingletonComponent::class)
@Module
object ModuleDatabase {
@Provides
@Singleton
fun provideAppDatabase(
@ApplicationContext context: Context,
userManager: UserManager,
): MyRoomDatabase {
return Room.databaseBuilder(context, MyRoomDatabase::class.java, LOCAL_DATABASE_NAME + "_${userManager.loggedInUid()}")
.addMigrations(...)
.fallbackToDestructiveMigration()
.build()
}
}
原来我需要创建一个符合要求的自定义Hilt组件:
- https://dagger.dev/hilt/custom-components.html
- https://medium.com/androiddevelopers/hilt-adding-components-to-the-hierarchy-96f207d6d92d
(请记住,这不适用于 WorkManager)
更新: 它实际上是通过避免 Hilt Singleton 作用域并自己构建它来工作的。这也适用于 WorkManager,因为 WorkManager 可以访问 SingletonComponents 内部的依赖项。
它基本上是这样工作的:
@InstallIn(SingletonComponent::class)
@Module
object ModuleDatabase {
private var databaseName: String by Delegates.observable(LOCAL_DATABASE_NAME) { _, oldValue, newValue ->
if (oldValue != newValue) {
databaseConnection?.let {
if (it.isOpen) {
it.close()
}
databaseConnection = null
// new connection opens with new name
}
}
}
private var databaseConnection: TemporyRoomDatabase? = null
private fun buildDatabase(context: Context, name: String): TemporyRoomDatabase {
Timber.e("Building database $databaseName")
return Room.databaseBuilder(context, TemporyRoomDatabase::class.java, name)
.addMigrations(*TemporyRoomDatabase_Migrations.build())
.fallbackToDestructiveMigration()
.build()
}
private fun getDatabaseConnection(context: Context, newDatabaseName: String): TemporyRoomDatabase {
return if (databaseName != newDatabaseName) {
databaseName = newDatabaseName
buildDatabase(context, databaseName).also {
databaseConnection = it
}
} else {
// use existing connection
Timber.e("Reusing database $databaseName")
databaseConnection ?: buildDatabase(context, databaseName)
}
}
@Provides
fun provideAppDatabase(
@ApplicationContext context: Context,
userManager: UserManager,
): TemporyRoomDatabase {
return getDatabaseConnection(context, LOCAL_DATABASE_NAME + "_${userManager.loggedInUidOrNull()}")
}
}