简体   繁体   English

分段。 getViewLifeCycleOwner 不会阻止多次调用 LiveData Observer

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

I use Clean Architecture, LiveData, Navigation component & Bottom Navigation view.我使用 Clean Architecture、LiveData、Navigation 组件和底部导航视图。
I am creating a simple application with three tabs .我正在创建一个带有三个选项卡的简单应用程序。 By default, the First tab Fragment loads user data using some API.默认情况下,第一个选项卡 Fragment 使用某些 API 加载用户数据。 When i go to another tabs and then return to the First tab Fragment, i see, that observe return a new data!当我转到另一个选项卡然后返回到第一个选项卡片段时,我看到, observe返回一个新数据!

I need observe not to return data again when I switch back to the first tab!当我切换回第一个选项卡时,我需要observe不要再次返回数据! 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. Ps 对于导航,我使用来自navigation-advanced-sample 的示例,并且在切换选项卡后没有调用onDestroy

First solution in the article Observe LiveData from ViewModel in Fragment said: Observe LiveData from ViewModel in Fragment一文中的第一个解决方案说:

One proper solution is to use getViewLifeCycleOwner() as LifeCycleOwer while observing LiveData inside onActivityCreated as follows.一种适当的解决方案是使用 getViewLifeCycleOwner() 作为 LifeCycleOwer,同时观察 onActivityCreated 中的 LiveData,如下所示。

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() .文章架构组件陷阱 - 第 1 部分中的第二个解决方案建议使用重置现有观察者在 onDestroyView() 中手动取消订阅观察者 But it doesn't work for me either...但它对我也不起作用......

ProfileFragment.kt 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 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 . ProfileRepository.kt class ProfileRepository @Inject constructor( private val loginUserDao: LoginUserDao, private val profileDao: ProfileDao, ) { class ProfileRepository @Inject 构造函数(私有 val loginUserDao:LoginUserDao,私有 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.这是因为Fragment Lifecycle 的工作方式。 When you move to and fro from a fragment onViewCreated() is called again.当您从片段来回移动时,将再次调用onViewCreated() In onViewCreated you're calling viewModel.getProfileLive() which returns the livedata upto from the repository and observe to it.onViewCreated您正在调用viewModel.getProfileLive() ,它从存储库返回实时数据并观察它。

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.由于每次移回FragmentonViewCreated()都会被调用,因此您对viewModel.getProfileLive()调用viewModel.getProfileLive()被调用,然后再次调用存储库,这再次触发了 Fragment 中的observe方法。

In order to solve this problem, create a LiveData variable in your ViewModel , set it to the returned Live Data from Repository .为了解决这个问题,在你的ViewModel 中创建一个LiveData变量,将它设置为从Repository返回的Live Data In the Fragment observe to the LiveData variable of your ViewModel not the one returned from Repository .Fragment 中观察ViewModelLiveData 变量,而不是从Repository返回的变量 That way, your observe method will get triggered on very first time and only when value of your data from repository changes.这样,您的observe方法将在第一次并且仅在您的存储库中的数据发生变化时触发。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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