在我的项目中,我使用 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”的匕首版本