简体   繁体   中英

Fragments observing livedata forever

The problem: I'm facing a problem with fragments and shared viewmodel LiveData ... The problem that is there is a FragmentA that update data in shared viewmodel and observe for it's changes then display the result for the user inside FragmentA , FragmentA can launch new instance of FragmentA to get new data and display it so the fragment launch it self and old instance gets added to the back stack, until here nothing is wrong the new instance updates LiveData in viewModel and displays the new data perfectly the problem that is when i popUpBackstack() return to FragmentA old instance the data displays in it is the data that new FragmentA instance gets it which means that old FragmentA instance still observing the data even if i remove the observers... this is general overview about the problem now i will show you fragments structure, the code and what the solution's that i'v tried.

Expected behavior: what i want to achieve is that when FragmentA launch it self and gets added to back stack stop observing the data in viewModel and when i back to it displays the old data that's all.

  • Fragment structure: i use one activity to hold all the fragments... MainActivity have FindMoviesFragment inside it there is a viewPager which holds FragmentMovies which launches MovieDetailsFragment and inside it there is ViewPager also which holds the fragments that displays the data from MoviesViewModel it will get clear when you see the code below.

This code shows how MovieDetailsFragment initialize MoviesViewModel and updates data in 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))
        }

    }

}

And inside MovieDetailsOne which is inside viewpager in MovieDetailsFragment i observe the data like this:

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

    }
}

Now What i tried is the following:

-Loacal scope for fragments

//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

-Observe once extinction function:

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

I know i took so long to explain:D but I'm trying to give you clear idea and sorry for you time <3... if you want any additional information about the code or the problem comment down below.

Finally i achieve the behavior that i want by doing some steps which is:

Attach ViewPager childs using childFragmentManager which means that android will treat viewpager fragments as childs for viewPager holder:

//instead of doing this
pagerAdapter = ViewPagerAdapter(activity?.supportragmentManager, lifecycle, fragmentList)

//do this
pagerAdapter = ViewPagerAdapter(childFragmentManager, lifecycle, fragmentList)

Define local viewModel for parent fragment like this:

//instead of doing this
moviesViewModel = ViewModelProvider(requireActivity()).get(MoviesViewModel::class.java)

//do this
moviesViewModel = ViewModelProvider(this).get(MoviesViewModel::class.java)

Define parent fragment scope viewModel in childs fragment:

//instead of doing this
moviesViewModel = ViewModelProvider(requireActivity()).get(MoviesViewModel::class.java)

//do this
private val moviesViewModel:MoviesViewModel by viewModels(
        {requireParentFragment()}
    )

by doing these steps parent fragment will stop observing data when it's destroyed and child fragments also.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM