Dagger 2 组件保留旧参考
Dagger 2 component keeping old reference
背景
我在应用程序中有多个匕首瞄准镜
- UserScope - 表示用户会话的范围
- ActivityScope - 每个 activity
的范围
- UserComponent - CoreComponent
的子组件
- UserManager - 创建 UserComponent
的单例
- HomeComponent - 依赖于 UserComponent
的组件
问题
我正在尝试更新 UserScope
下的用户对象,但不知何故对象更新没有反映在它的依赖组件上。因此,一旦更新屏幕更新了用户对象,并且 backstack 中的 activity 接收到更新事件,我想在我的匕首依赖项中反映该更新。
我试图使 UserManager
中的现有组件无效,但看起来依赖组件仍然保留它的引用。
总结
用户登录并打开主屏幕。此时我的用户管理器已经使用从 API 调用获得的用户对象创建了用户组件。
主屏幕打开更新屏幕,通过在 UserManager
中重新创建新的 userComponent
来更新用户。这也会触发一个事件,告诉主屏幕获取更新的用户对象。
主屏幕收到事件。但是,HomeViewModel(Screen in backstack) 仍然指代由匕首注入的旧 User
对象。
问题
- 这是因为
HomeComponent
是一个独立的组件吗?所以,它保留了一个旧的引用?
- 有什么办法可以实现这个更新吗?
- 最好不使用
SubComponent
。因为我使用的是多模块(动态功能)设置,如果我将其视为单独的组件,它会帮助我。
核心组件
@Singleton
@Component
interface CoreComponent {
val userBuilder: UserComponent.Builder
val userManager: UserManager
fun providerContext(): Context
fun inject(activity: UserActivity)
@Component.Factory
interface Factory {
fun create(
@BindsInstance applicationContext: Context
): CoreComponent
}
}
用户组件
@UserScope
@Subcomponent
interface UserComponent {
val userManager: UserManager
@UserScope
val user: User
@Subcomponent.Builder
interface Builder {
fun bindUser(@BindsInstance user: User): Builder
fun build(): UserComponent
}
}
用户管理器
@Singleton
class UserManager @Inject constructor(private val userBuilder: UserComponent.Builder) {
var userComponent: UserComponent? = null
private set
init {
// creating as a dummy reference
createUser(User("1", "1"))
}
fun createUser(user: User) {
userComponent = null
userComponent = userBuilder.bindUser(user).build()
}
fun logout() {
userComponent = null
}
}
主页组件
@Component(
modules = [HomeModule::class],
dependencies = [UserComponent::class]
)
@ActivityScope
interface HomeComponent {
fun inject(activity: HomeActivity)
@Component.Factory
interface Factory {
fun create(
@BindsInstance applicationContext: HomeActivity,
coreComponent: UserComponent,
): HomeComponent
}
}
家庭模块
@Module
class HomeModule {
@ActivityScope
@Provides
fun provideVM(activity: HomeActivity, user: User): HomeViewModel {
val vm by activity.scopedComponent {
HomeViewModel(user)
}
return vm
}
}
如果您有组件 A -> B -> C
并决定将 B
换成 B*
那么您还需要删除创建的所有对象 by/using B
或依赖于 B
的任何东西,因此任何子组件、组件依赖项、对象等
在此示例中,您还需要重新创建 C
(这次使用 B*
)以获得 C*
(使用新的依赖项)。现在您有了新组件 A -> B* -> C*
,但是使用 B/C
创建的代码 (Activity/Fragment/ViewModel) 也需要“更新”。由于以后“更新”依赖项是不可行的,因此最简单的方法是重新创建所有依赖项,这次使用 B*/C*
进行注入。
例如
在我允许在用户之间切换的一个应用程序中,我有我的主要 Activity“Singleton -> Activity”范围,引用用户管理器并显示开关 UI。实际的应用程序内容(每个用户)包含在 UserFragment
中,每当用户更改时我都会将其作为一个整体替换,因为此片段(以及其中的所有 UI)是 Singleton -> Activity -> User -> Fragment
范围。
由于包括用户管理器在内的 Singleton 不会更改,因此我不必重新创建 Activity,因此我需要做的就是重新创建受影响的部分(在我的例子中是 Fragment)。
背景
我在应用程序中有多个匕首瞄准镜
- UserScope - 表示用户会话的范围
- ActivityScope - 每个 activity 的范围
- UserComponent - CoreComponent 的子组件
- UserManager - 创建 UserComponent 的单例
- HomeComponent - 依赖于 UserComponent 的组件
问题
我正在尝试更新 UserScope
下的用户对象,但不知何故对象更新没有反映在它的依赖组件上。因此,一旦更新屏幕更新了用户对象,并且 backstack 中的 activity 接收到更新事件,我想在我的匕首依赖项中反映该更新。
我试图使 UserManager
中的现有组件无效,但看起来依赖组件仍然保留它的引用。
总结
用户登录并打开主屏幕。此时我的用户管理器已经使用从 API 调用获得的用户对象创建了用户组件。
主屏幕打开更新屏幕,通过在
UserManager
中重新创建新的userComponent
来更新用户。这也会触发一个事件,告诉主屏幕获取更新的用户对象。主屏幕收到事件。但是,HomeViewModel(Screen in backstack) 仍然指代由匕首注入的旧
User
对象。
问题
- 这是因为
HomeComponent
是一个独立的组件吗?所以,它保留了一个旧的引用? - 有什么办法可以实现这个更新吗?
- 最好不使用
SubComponent
。因为我使用的是多模块(动态功能)设置,如果我将其视为单独的组件,它会帮助我。
- 最好不使用
核心组件
@Singleton
@Component
interface CoreComponent {
val userBuilder: UserComponent.Builder
val userManager: UserManager
fun providerContext(): Context
fun inject(activity: UserActivity)
@Component.Factory
interface Factory {
fun create(
@BindsInstance applicationContext: Context
): CoreComponent
}
}
用户组件
@UserScope
@Subcomponent
interface UserComponent {
val userManager: UserManager
@UserScope
val user: User
@Subcomponent.Builder
interface Builder {
fun bindUser(@BindsInstance user: User): Builder
fun build(): UserComponent
}
}
用户管理器
@Singleton
class UserManager @Inject constructor(private val userBuilder: UserComponent.Builder) {
var userComponent: UserComponent? = null
private set
init {
// creating as a dummy reference
createUser(User("1", "1"))
}
fun createUser(user: User) {
userComponent = null
userComponent = userBuilder.bindUser(user).build()
}
fun logout() {
userComponent = null
}
}
主页组件
@Component(
modules = [HomeModule::class],
dependencies = [UserComponent::class]
)
@ActivityScope
interface HomeComponent {
fun inject(activity: HomeActivity)
@Component.Factory
interface Factory {
fun create(
@BindsInstance applicationContext: HomeActivity,
coreComponent: UserComponent,
): HomeComponent
}
}
家庭模块
@Module
class HomeModule {
@ActivityScope
@Provides
fun provideVM(activity: HomeActivity, user: User): HomeViewModel {
val vm by activity.scopedComponent {
HomeViewModel(user)
}
return vm
}
}
如果您有组件 A -> B -> C
并决定将 B
换成 B*
那么您还需要删除创建的所有对象 by/using B
或依赖于 B
的任何东西,因此任何子组件、组件依赖项、对象等
在此示例中,您还需要重新创建 C
(这次使用 B*
)以获得 C*
(使用新的依赖项)。现在您有了新组件 A -> B* -> C*
,但是使用 B/C
创建的代码 (Activity/Fragment/ViewModel) 也需要“更新”。由于以后“更新”依赖项是不可行的,因此最简单的方法是重新创建所有依赖项,这次使用 B*/C*
进行注入。
例如
在我允许在用户之间切换的一个应用程序中,我有我的主要 Activity“Singleton -> Activity”范围,引用用户管理器并显示开关 UI。实际的应用程序内容(每个用户)包含在 UserFragment
中,每当用户更改时我都会将其作为一个整体替换,因为此片段(以及其中的所有 UI)是 Singleton -> Activity -> User -> Fragment
范围。
由于包括用户管理器在内的 Singleton 不会更改,因此我不必重新创建 Activity,因此我需要做的就是重新创建受影响的部分(在我的例子中是 Fragment)。