简体   繁体   中英

Fragment. getViewLifeCycleOwner doesn't prevent multiple calls of LiveData Observer

I use Clean Architecture, LiveData, Navigation component & Bottom Navigation view.
I am creating a simple application with three tabs . By default, the First tab Fragment loads user data using some API. When i go to another tabs and then return to the First tab Fragment, i see, that observe return a new data!

I need observe not to return data again when I switch back to the first tab! what am I doing wrong? Could you help me please?

Ps For navigation i use sample from navigation-advanced-sample and after switching tabs onDestroy is not called.

First solution in the article Observe LiveData from ViewModel in Fragment said:

One proper solution is to use getViewLifeCycleOwner() as LifeCycleOwer while observing LiveData inside onActivityCreated as follows.

I use following code, but it's not work for me:

override fun onActivityCreated(savedInstanceState: Bundle?) {
    super.onActivityCreated(savedInstanceState)
    Timber.d("onActivityCreated")
    viewModel.getProfileLive().observe(viewLifecycleOwner, observer)
}

Second solution in the article Architecture Components pitfalls — Part 1 recommends using Resetting an existing observer and Manually unsubscribing the observer in onDestroyView() . But it doesn't work for me either...

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")
    }
}

ProfileFragmentViewModel.kt

class ProfileFragmentViewModel @Inject constructor(
    private val profileUseCase: ProfileUseCase
) : ViewModel() {

    init {
        Timber.d("Init profile VM")
    }

    fun getProfileLive() = profileUseCase.getProfile()
}

ProfileUseCase

class ProfileUseCase @Inject constructor(
    private val profileRepository: ProfileRepository
) {

    fun getProfile(): LiveData<Resource<Profile>> {
        return profileRepository.getProfile()
    }
}

ProfileRepository.kt . class ProfileRepository @Inject constructor( private val loginUserDao: LoginUserDao, private val profileDao: ProfileDao, ) {

fun getProfile(): LiveData<Resource<Profile>> =
    liveData(Dispatchers.IO)
    {
        emit(Resource.loading(data = null))

        val profile = profileDao.getProfile()

        // Emit Success result...
    }

}

It's because of how Fragment Lifecycle works. When you move to and fro from a fragment onViewCreated() is called again. In onViewCreated you're calling viewModel.getProfileLive() which returns the livedata upto from the repository and observe to it.

Since onViewCreated() gets called everytime when you move back to the Fragment so is your call to viewModel.getProfileLive() and in turn the repository gets called again which again triggers the observe method in your Fragment.

In order to solve this problem, create a LiveData variable in your ViewModel , set it to the returned Live Data from Repository . In the Fragment observe to the LiveData variable of your ViewModel not the one returned from Repository . That way, your observe method will get triggered on very first time and only when value of your data from repository changes.

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