Android ViewModel 泄漏

rOO*_*b85 1 android leakcanary android-room android-viewmodel

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

这就是 LeakCanary 显示的原因

这是获取 ViewModel 的 Fragment

viewModel = ViewModelProvider(this).get(ViewBreederViewModel::class.java).apply {
        getStrains(arguments?.getString(BREEDER_ID_KEY, "")!!)
    }
Run Code Online (Sandbox Code Playgroud)

这是视图模型

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
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

}

这是 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)
    }
Run Code Online (Sandbox Code Playgroud)

}

这是 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
    }
}
Run Code Online (Sandbox Code Playgroud)

}

ian*_*ake 5

您正在使用observeForever,顾名思义,它将永远持续观察,即使在您的 ViewModel 被清除之后也是如此。Room 不需要使用suspend返回 a 的 DAO 方法LiveData,而且在任何情况下这都不是正确的方法 -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
    }
}
Run Code Online (Sandbox Code Playgroud)

你将getMinimalStrains成为:

fun getMinimalStrains(breederId: String): LiveData<List<MinimalStrain>> =
    dao.getMinimalStrains(breederId)
Run Code Online (Sandbox Code Playgroud)

breederId您可以通过在 UI 中设置并strainList像以前一样观察您来使用它:

viewModel = ViewModelProvider(this).get(ViewBreederViewModel::class.java).apply {
    setBreederId(arguments?.getString(BREEDER_ID_KEY, "")!!)
}
viewModel.strainList.observe(viewLifecycleOwner) { strainList ->
  // use your updated list
}
Run Code Online (Sandbox Code Playgroud)

如果您正在使用ViewModel 的 Saved State 模块(如果您使用的是最新的稳定片段/活动库,则这是默认的),那么您可以使用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)
        }
}
Run Code Online (Sandbox Code Playgroud)

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

viewModel = ViewModelProvider(this).get(ViewBreederViewModel::class.java)
viewModel.strainList.observe(viewLifecycleOwner) { strainList ->
  // use your updated list
}
Run Code Online (Sandbox Code Playgroud)

如果您使用该fragment-ktx工件,您可以进一步简化为:

// Move this to where you declare viewModel
val viewModel: ViewBreederViewModel by viewModels()

viewModel.strainList.observe(viewLifecycleOwner) { strainList ->
  // use your updated list
}
Run Code Online (Sandbox Code Playgroud)