如何使用 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组件:

(请记住,这不适用于 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()}")
    }
}