0

在我的项目中,我使用 Dagger2 将 ViewModels 注入片段中。

  override val viewModel: AllStockListTabViewModel by viewModels ({this}, {viewModelFactory})

为了简要解释我的情况,我有一个使用片段状态适配器的片段,其中包含两个片段。为了方便起见,我将在片段状态适配器片段 B 和片段 C 中调用父片段片段 A 和子片段。

通常,在测试应用程序时,用户会花时间在包含 recyclerview 的片段 B 中。当用户点击其中一个项目时,它会导致一个带有一些详细信息的不同片段。当用户输入该详细信息片段时,持有该项目的片段 B 将通过 onPause() 和 onStop()。同时,在片段 C 中调用了 onStop()。

关键是,如果用户在片段 B(由片段 A 包含)中花费了足够的时间,片段 C 将被销毁,这并不奇怪,因为我知道这是片段状态适配器的意图。它应该在不可见时摆脱一些碎片。

我的问题是,当片段 C 被破坏时,与之关联的视图模型不会被破坏。这很糟糕,因为现在当用户转到仍然引用旧视图模型的片段 C 时,应用程序不会向片段提供任何数据,因为当调用 onDestroy() 时,片段 C 的视图模型被清除,因此 viewmodelscope.launch 是不工作。

我也想过不使用 viewmodelscope (使用 coroutinescope 代替),但这不是问题。我很好奇和渴望知道为什么片段C的视图模型,范围为片段C的生命周期没有被破坏。(我想在片段C消亡时摆脱旧的视图模型并获得新的视图模型实例)

请理解我笨拙的措辞和我缺乏知识可能会引起一些混乱。我是匕首新手。请参阅下面的代码以更好地理解。

应用组件.kt

@Singleton
@Component(
  modules = [
    AndroidSupportInjectionModule::class,
    ActivityBindingModule::class,
    RepositoryModule::class,
    DataSourceModule::class,
    ServiceModule::class,
    DaoModule::class,
    ViewModelModule::class,
  ]
)

ViewModelModule.kt

@MapKey
@Target(
  AnnotationTarget.FUNCTION,
  AnnotationTarget.PROPERTY_GETTER,
  AnnotationTarget.PROPERTY_SETTER
)
annotation class ViewModelKey(val value: KClass<out ViewModel>)

@Module
abstract class ViewModelModule {

  @Binds
  @IntoMap
  @ViewModelKey(AllStockListTabViewModel::class)
  abstract fun bindAllStockListTabViewModel(allStockListTabViewModel: AllStockListTabViewModel): ViewModel
}

视图模型工厂

@Singleton
class ViewModelFactory @Inject constructor(
  private val viewModelMap: Map<Class<out ViewModel>, @JvmSuppressWildcards Provider<ViewModel>>
) : ViewModelProvider.Factory {
  override fun <T : ViewModel?> create(modelClass: Class<T>): T {
    return viewModelMap[modelClass]?.get() as T
  }
}

分段

class AllStockListTabFragment @Inject constructor() :
  ViewModelFragment<FragmentAllStockListBinding>(R.layout.fragment_all_stock_list) {

  @Inject
  lateinit var viewModelFactory: ViewModelFactory

  override val viewModel: AllStockListTabViewModel by viewModels ({this}, {viewModelFactory})
}

适配器

    tradingTabAdapter = TradingTabAdapter(
      this.childFragmentManager,
      this.lifecycle,
      tradingStateTabFragment,
      allStockListTabFragment
    )
class TradingTabAdapter @Inject constructor(
  fragmentManager: FragmentManager,
  lifecycle: Lifecycle,
  private val tradingStateTabFragment: TradingStateTabFragment,
  private val allStockListTabFragment: AllStockListTabFragment
) : FragmentStateAdapter(fragmentManager, lifecycle) {

  override fun createFragment(position: Int): Fragment =
    when (position) {
      0 -> tradingStateTabFragment
      else -> allStockListTabFragment
    }

  override fun getItemCount(): Int = 2
}

子组件

@FragmentScope
@Subcomponent(
  modules = [
    TradingTabBindingModule::class,
    TradingTabModule::class,
    EventModule::class,
    UseCaseModule::class
  ]
)

适配器模块

@Module
class TradingTabModule {

  @Provides
  fun provideTradingTabAdapter(
    fragment: TradingTabFragment,
    allStockListTabFragment: AllStockListTabFragment,
    tradingStateTabFragment: TradingStateTabFragment
  ) = TradingTabAdapter(
    fragment.childFragmentManager,
    fragment.lifecycle,
    tradingStateTabFragment,
    allStockListTabFragment
  )

我发现当片段 C 被销毁并再次创建时,没有调用 ViewModelFactory 的 create 方法。我认为这是因为我正在使用 viewmodel 的延迟初始化,这就是 ViewModelLazy 的工作方式。它缓存视图模型并仅在缓存为空时调用工厂的创建方法。我猜发生的事情是片段 C 的旧视图模型仍在引用死视图模型(它在 viewModelStore.onclear 中幸存下来)。我在片段 C 的视图模型的 init 块中放置了一条日志语句,我可以看到它仅在片段 C 被创建的第一次被调用,并且即使片段 C 被销毁并再次创建,也不再被调用。

非常感谢您耐心阅读所有这些哈哈。因此,我需要任何有经验的 Android 专家的帮助,他们可能能够提供一些见解。

我的目标:使用片段的生命周期销毁和重新创建视图模型。我想避免由于未使用的僵尸视图模型而导致内存泄漏。

当前情况:视图模型永远不会被销毁,重生的片段仍然引用旧的视图模型,因此延迟初始化会保留旧视图模型的缓存,而不是触发 ViewModelFactory 的创建方法。

- 编辑 -

使用“com.google.dagger:dagger-android:2.37”的匕首版本

4

1 回答 1

0

由于您的 ViewModel 与您的 Activity 相关联,因此在 Fragment 被销毁时它不会被销毁。

@ViewModelKey(MainActivityViewModel::class)
abstract fun bindMainActivityViewModel(mainActivityViewModel: MainActivityViewModel): ViewModel

您可以查看此答案,该答案解释了如何将 ViewModel 与 Fragment 一起使用。

如何在片段中使用 ViewModel?

于 2021-11-26T04:51:38.910 回答