Android ViewModel 正在泄漏

问题描述

LeakCanary 告诉我我的一个 viewmodel 正在泄漏,但玩了 2 天后我无法让泄漏消失。

Here is why LeakCanary shows

这里是获取 viewmodel 的 Fragment

viewmodel = viewmodelProvider(this).get(ViewBreederviewmodel::class.java).apply {
        getStrains(arguments?.getString(BREEDER_ID_KEY,"")!!)
    }

这是视图模型

class ViewBreederviewmodel(application: Application) : Androidviewmodel(application) {

private val breederRepository = BreederRepository(application)
val strainList = mutablelivedata<List<MinimalStrain>>()

fun getStrains(breederId: String) {
    viewmodelScope.launch {
        breederRepository.getMinimalStrains(breederId).observeForever {
            strainList.value = it
        }
    }
}

}

这是 BreederRepository:

class BreederRepository(context: Context) {

private val dao: BreederDao
private val breederApi = RetrofitClientInstance.getInstance(context).breederAndStrainIdsApi

init {
    val database: Db = Db.getInstance(
        context
    )!!
    dao = database.breederDao()
}

suspend fun getMinimalStrains(breederId: String): LiveData<List<MinimalStrain>> =
    withContext(dispatchers.IO) {
        dao.getMinimalStrains(breederId)
    }

}

这里是 Db 类

@Database(
entities = [Breeder::class,Strain::class],version = 1,exportSchema = true)
@TypeConverters(RoomDateConverter::class)

abstract class Db : RoomDatabase() {

abstract fun breederDao(): BreederDao

companion object {
    private var instance: Db? = null

    @JvmStatic
    fun getInstance(context: Context): Db? {
        if (instance == null) {
            synchronized(Db::class) {
                instance = Room.databaseBuilder(
                    context.applicationContext,Db::class.java,"seedfinder_db"
                )
                    .build()
            }
        }
        return instance
    }
}

}

解决方法

您正在使用 end="",顾名思义,即使在您的 ViewModel 被清除后,它也会永远保持观察。 Room 不需要为返回 observeForever 的 DAO 方法使用 suspend 方法,这在任何情况下都不是正确的方法 - LiveData 已经是异步的。

相反,您应该是 transforming your LiveData,使用您的 LiveData 作为您的 breederId LiveData 的输入:

strainList

您的 class ViewBreederViewModel(application: Application) : AndroidViewModel(application) { private val breederRepository = BreederRepository(application) private val currentBreederId = MutableLiveData<String>() // Here we use the switchMap method from the lifecycle-livedata-ktx artifact val strainList: LiveData<String> = currentBreederId.switchMap { breederId -> breederRepository.getMinimalStrains(breederId) } private fun setBreederId(breederId: String) { currentBreederId.value = breederId } } 的位置:

getMinimalStrains

您可以通过在 UI 中设置 fun getMinimalStrains(breederId: String): LiveData<List<MinimalStrain>> = dao.getMinimalStrains(breederId) 并像以前一样观察 breederId 来使用它:

strainList

如果您使用 Saved State module for ViewModels(如果您使用最新的稳定 Fragments / Activity 库,这是默认设置),那么您可以使用 viewModel = ViewModelProvider(this).get(ViewBreederViewModel::class.java).apply { setBreederId(arguments?.getString(BREEDER_ID_KEY,"")!!) } viewModel.strainList.observe(viewLifecycleOwner) { strainList -> // use your updated list } ,它会根据您的 Fragment 的参数自动填充并完全跳过 SavedStateHandle

setBreederId()

这意味着您的代码可以简单地变成:

class ViewBreederViewModel(
    application: Application,savedStateHandle: SavedStateHandle
) : AndroidViewModel(application) {

    private val breederRepository = BreederRepository(application)

    // Here we use the switchMap method from the lifecycle-livedata-ktx artifact
    val strainList: LiveData<String> = savedStateHandle
        .getLiveData(BREEDER_ID_KEY) // Automatically populated from arguments
        .switchMap {
            breederId -> breederRepository.getMinimalStrains(breederId)
        }
}

如果您使用 viewModel = ViewModelProvider(this).get(ViewBreederViewModel::class.java) viewModel.strainList.observe(viewLifecycleOwner) { strainList -> // use your updated list } 工件,您可以将其进一步简化为:

fragment-ktx