Android 碎片。getViewLifeCycleOwner没有';t防止多次调用LiveData Observer
我使用干净的体系结构、LiveData、导航组件和底部导航视图。Android 碎片。getViewLifeCycleOwner没有';t防止多次调用LiveData Observer,android,kotlin,android-fragments,navigation,android-livedata,Android,Kotlin,Android Fragments,Navigation,Android Livedata,我使用干净的体系结构、LiveData、导航组件和底部导航视图。 我正在创建一个带有三个选项卡的简单应用程序。默认情况下,第一个选项卡片段使用一些API加载用户数据。当我转到另一个选项卡,然后返回到第一个选项卡片段时,我看到,observe返回一个新数据 我需要观察在切换回第一个选项卡时不再返回数据!我做错了什么?你能帮我吗 注意:对于导航,我使用切换选项卡前后的示例onDestroy未被调用 第一个解决方案在文章中说: 一个合适的解决方案是使用getViewLifeCycleOwner()作为
我正在创建一个带有三个选项卡的简单应用程序。默认情况下,第一个选项卡片段使用一些API加载用户数据。当我转到另一个选项卡,然后返回到第一个选项卡片段时,我看到,
observe
返回一个新数据
我需要观察
在切换回第一个选项卡时不再返回数据!我做错了什么?你能帮我吗
注意:对于导航,我使用切换选项卡前后的示例onDestroy
未被调用
第一个解决方案在文章中说:
一个合适的解决方案是使用getViewLifeCycleOwner()作为生命周期电源,同时观察ActivityCreated中的LiveData,如下所示
我使用以下代码,但它不适用于我:
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
Timber.d("onActivityCreated")
viewModel.getProfileLive().observe(viewLifecycleOwner, observer)
}
文章中的第二个解决方案建议在onDestroyView()中使用重置现有观察者并手动取消订阅观察者。但这对我也不起作用
ProfileFragment.kt
class ProfileFragment : DaggerFragment() {
@Inject
lateinit var viewModel: ProfileFragmentViewModel
private val observer = Observer<Resource<Profile>> {
when (it.status) {
Resource.Status.LOADING -> {
Timber.i("Loading...")
}
Resource.Status.SUCCESS -> {
Timber.i("Success: %s", it.data)
}
Resource.Status.ERROR -> {
Timber.i("Error: %s", it.message)
}
}
};
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Timber.d("onCreate")
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
Timber.d("onCreateView")
return inflater.inflate(R.layout.fragment_profile, container, false)
}
fun <T> LiveData<T>.reObserve(owner: LifecycleOwner, observer: Observer<T>) {
removeObserver(observer)
observe(owner, observer)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
Timber.d("onViewCreated")
viewModel.getProfileLive().observe(viewLifecycleOwner, observer)
// viewModel.getProfileLive().reObserve(viewLifecycleOwner, observer)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
Timber.d("onActivityCreated")
}
override fun onDestroyView() {
super.onDestroyView()
Timber.d("onDestroyView")
// viewModel.getProfileLive().removeObserver(observer)
}
override fun onDestroy() {
super.onDestroy()
Timber.d("onDestroy")
}
override fun onDetach() {
super.onDetach()
Timber.d("onDetach")
}
}
class ProfileFragmentViewModel @Inject constructor(
private val profileUseCase: ProfileUseCase
) : ViewModel() {
init {
Timber.d("Init profile VM")
}
fun getProfileLive() = profileUseCase.getProfile()
}
档案用例
class ProfileUseCase @Inject constructor(
private val profileRepository: ProfileRepository
) {
fun getProfile(): LiveData<Resource<Profile>> {
return profileRepository.getProfile()
}
}
class ProfileUseCase@Inject构造函数(
private val profileRepository:profileRepository
) {
fun getProfile():LiveData{
返回profileRepository.getProfile()
}
}
ProfileRepository.kt。
类ProfileRepository@Inject构造函数(
私人val loginUserDao:loginUserDao,
private val profileDao:profileDao,
){
fun getProfile():LiveData=
liveData(Dispatchers.IO)
{
发出(Resource.loading(数据=null))
val profile=profileDao.getProfile()
//发射成功结果。。。
}
}这是因为片段生命周期的工作方式。当您从片段来回移动时,会再次调用
onViewCreated()
。在onViewCreated
中,您正在调用viewModel.getProfileLive()
,它将livedata从存储库返回到并观察到它
由于每次返回到片段时都会调用onViewCreated()
,因此对viewModel.getProfileLive()
的调用也是如此,而存储库也会再次被调用,从而再次触发片段中的observe
方法
为了解决这个问题,,
在您的视图模型中创建一个LiveData变量,将其设置为从存储库中返回的LiveData。
在片段中观察视图模型的LiveData变量,而不是从存储库返回的变量。
这样,您的
observe
方法将在第一次触发,并且仅当存储库中的数据的值发生更改时才会触发。LiveData始终缓存最后的结果并重新交付,这与您的观察者的生命周期无关(使用getViewLifecycleOwner()
时已经正确)。@ianhanniballake以及解决此问题的正确方法是什么?“我明白了,该观察返回一个新数据”这到底有什么问题?“我需要观察不再返回数据”为什么?@Tobi让我们假设你在使用Instagram。每次切换选项卡时,都会发出一个新的请求来加载提要和其他项目。在我的简单应用程序中,导航应该以类似的方式工作。如果我理解正确,你希望在切换选项卡时再次调用API,对吗?长话短说:在构造函数中加载数据如果你的viewmodel没有出现在你的长篇故事片段中,这样他就可以理解错在哪里了。当然,有时用一句话来总结它,以避免潜在的混乱;)谢谢!如果我需要更新数据,在这种情况下,正确的方法是什么?
fun getProfile(): LiveData<Resource<Profile>> =
liveData(Dispatchers.IO)
{
emit(Resource.loading(data = null))
val profile = profileDao.getProfile()
// Emit Success result...
}