【问题标题】: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,但我试图给你一个清晰的想法,对不起你的时间

【问题讨论】:

    标签: android kotlin fragment android-livedata android-viewmodel


    【解决方案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()}
        )
    
    

    通过执行这些步骤,父片段将在数据被销毁时停止观察数据,子片段也将停止观察。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-08-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-22
      • 2020-04-02
      • 1970-01-01
      • 2019-08-04
      相关资源
      最近更新 更多