永远观察实时数据的片段
Posted
技术标签:
【中文标题】永远观察实时数据的片段【英文标题】:Fragments observing livedata forever 【发布时间】:2021-07-28 16:59:47 【问题描述】:问题:我面临片段和共享的问题 viewmodel LiveData ...问题是存在 FragmentA更新共享 viewmodel 中的数据并观察其变化,然后在 FragmentA 中为用户显示结果,FragmentA 可以启动 的新实例>FragmentA 获取新数据并显示它,以便片段自行启动并将旧实例添加到后台堆栈,直到这里没有任何问题,新实例更新 LiveData 中的 viewModel 并完美显示新数据问题是当我 popUpBackstack() 返回到 FragmentA 旧实例时,其中显示的数据是新的数据FragmentA 实例得到它,这意味着即使我移除了观察者,旧的 FragmentA 实例仍在观察数据......这是一般情况关于问题的概述,现在我将向您展示片段结构、代码以及我尝试过的解决方案。
预期行为:我想要实现的是,当 FragmentA 自行启动并添加到后端堆栈时,停止观察 viewModel 中的数据,当我返回它时仅显示旧数据。
片段结构:我使用一个活动来保存所有片段... MainActivity 里面有 FindMoviesFragment 里面有一个 viewPager FragmentMovies 启动 MovieDetailsFragment 并在其中有 ViewPager 还保存显示来自 MoviesViewModel 的数据的片段看到下面的代码就明白了。这段代码展示了MovieDetailsFragment如何初始化MoviesViewModel并更新viewmodel中的数据:
class MovieDetailsFragment:Fragment(R.layout.movie_details_fragment)
private val args: MovieDetailsFragmentArgs by navArgs()
private val fragmentList:ArrayList<Fragment> by lazy
arrayListOf(MovieDetailsOneFragment(args.movieId), MovieDetailsTwoFragment(),MovieDetailsThreeFragment())
private lateinit var pagerAdapter: ViewPagerAdapter
private lateinit var moviesViewModel: MoviesViewModel
override fun onCreate(savedInstanceState: Bundle?)
super.onCreate(savedInstanceState)
moviesViewModel = ViewModelProvider(requireActivity()).get(MoviesViewModel::class.java)
//these two lines updates the data in viewmodel
moviesViewModel.getMovieDetails(args.movieId)
moviesViewModel.changeMovieID(args.movieId)
//---------------------MoviesViewModel------------------//
class MoviesViewModel constructor(private val repo:Repository):ViewModel()
private val _currentMovieDetails = MutableLiveData<Resource<MovieDetails>>()
val currentMovieDetails :LiveData<Resource<MovieDetails>>
get() = _currentMovieDetails
fun getMovieDetails(movieID:Int) = viewModelScope.launch
_currentMovieDetails.postValue(Resource.loading(null))
val result = repo.getMovieDetails(movieID)
if(result.isSuccessful)
_currentMovieDetails.postValue(Resource.success(result.body()))
else
_currentMovieDetails.postValue(Resource.error(result.errorBody().toString(),null))
在 MovieDetailsFragment 的 viewpager 内的 MovieDetailsOne 中,我观察到这样的数据:
class MovieDetailsOneFragment(private val movieId: Int):Fragment(R.layout.movie_details_one)
private lateinit var moviesViewModel:MoviesViewModel
override fun onCreate(savedInstanceState: Bundle?)
super.onCreate(savedInstanceState)
//this is how i define viewModel(global scope)
moviesViewModel = ViewModelProvider(requireActivity()).get(MoviesViewModel::class.java)
override fun onViewCreated(view: View, savedInstanceState: Bundle?)
super.onViewCreated(view, savedInstanceState)
//in this method i observe on changes in currentMovieDetils liveData
subscribeToObservers()
//i try to call this from on create and nothing changes too
现在我尝试的是以下内容:
-片段的本地范围
//Define viewModel like this in MovieDetilsFragment
moviesViewModel = ViewModelProvider(this).get(MoviesViewModel::class.java)
//And in MovieDetailsOne like this
moviesViewModel = ViewModelProvider(this).get(MoviesViewModel::class.java)
//and this doesn't work MovieDetailsOne don't observe any changes
-观察一次消光函数:
fun <T> LiveData<T>.observeOnce(lifecycleOwner: LifecycleOwner, observer: Observer<T>)
observe(lifecycleOwner, object : Observer<T>
override fun onChanged(t: T?)
observer.onChanged(t)
removeObserver(this)
)
//and this doesn't work MovieDetailsOne don't observe first time it's lanches
我知道我花了很长时间来解释:D,但我试图给你一个清晰的想法,对不起你的时间
【问题讨论】:
【参考方案1】:最后我通过一些步骤实现了我想要的行为:
使用 childFragmentManager 附加 ViewPager 孩子,这意味着 android 会将 viewpager 片段视为 viewPager 持有者的孩子:
//instead of doing this
pagerAdapter = ViewPagerAdapter(activity?.supportragmentManager, lifecycle, fragmentList)
//do this
pagerAdapter = ViewPagerAdapter(childFragmentManager, lifecycle, fragmentList)
为父片段定义本地 viewModel,如下所示:
//instead of doing this
moviesViewModel = ViewModelProvider(requireActivity()).get(MoviesViewModel::class.java)
//do this
moviesViewModel = ViewModelProvider(this).get(MoviesViewModel::class.java)
在子片段中定义父片段范围viewModel:
//instead of doing this
moviesViewModel = ViewModelProvider(requireActivity()).get(MoviesViewModel::class.java)
//do this
private val moviesViewModel:MoviesViewModel by viewModels(
requireParentFragment()
)
通过执行这些步骤,父片段将在数据被销毁时停止观察数据,子片段也将停止观察。
【讨论】:
以上是关于永远观察实时数据的片段的主要内容,如果未能解决你的问题,请参考以下文章
如何实现一个 Firebase 监听器来观察数据库的实时变化