一、在Fragment的onViewCreated中订阅Observer时。
1、使用this表示Observer的生命周期和Framgent绑定。
2、Fragment出栈时,并不会走到OnDestory方法,导致旧的订阅不会被清除。
3、重建Framgent会再次订阅Observer。
4、这样造成了Observer的重复订阅
总结:应当使用Fragment的viewLifecycleOwner订阅Observer,不应当使用this替代。
data.observe(viewLifecycleOwner, {
Log.e("fragment", "count: ${data.value}")
})
二、在Activity或者Framgnet中使用Flow或者StateFlow
1、直接启用协程由于无法控制创建泄露的销毁,会导致内存泄露。
2、使用lifecycle-runtime-ktx
库提供的 lifecycleOwner.lifecycleScope,虽然开启的协程可以随着
在Activity或者Framgnet的生命周期开始和结束,有效解决了内存泄露。但是会立即启动协程,无法在仅当UI处于前台时,才执行协程,造成资源浪费。
3、使用LaunchWhenStarted/LaunchWhenResumed
启动协程,有效结果了内存泄露。进入指定周期启用协程,进入stop周期之后便挂起了协程,折让节省了资源,但是挂起并不代表协程销毁,此时上游仍然在处理数据,虽然不会发射到界面, 因为资源浪费的问题未彻底解决。
4、lifecycle-runtime-ktx 提供了lifecyle.repeatOnLifecycle 进入指定生命周期启用协程,进入Stop之后销毁协程,重建时将会重新创建协程。这样便解决了以上所有问题。
viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.lifecycle.repeatOnLifecycle(STARTED) {
viewMode.stateFlow.collect { ... }
}
}
lifecycleScope.launch {
viewMode.stateFlow
.flowWithLifecycle(this, Lifecycle.State.STARTED)
.collect { ... }
}
总结:使用repeatOnLifecycle或者flowWithLifecycle,后者是前者的封装。
三、ViewModel初次数据请求
1、在onViewCreated中进行首次数据加载。这样当View的重建(横竖屏切换)会造成数据的重复请求。由于ViewModle的生命周期长于View,可以跨越View的生命周期,因此重复请求是对资源的浪费。
//在ViewModel的init中初始化数据,此时数据的初次加载是在ViewModel创建时完成。
class TasksViewModel: ViewModel() {
private val _tasks = MutableLiveData<List<Task>>()
val tasks: LiveData<List<Task>> = _uiState
init {
viewModelScope.launch {
_tasks.value = withContext(Dispatchers.IO){
TasksRepository.fetchTasks()
}
}
}
}