ViewModel再次使用distinctUntilChanged()重新获取数据

问题描述 投票:-2回答:1

我有一个片段,我想对其数据进行一次读取,我已经使用distinctUntilChanged()来读取一次,因为在此片段期间我的位置没有改变。

片段

 private val viewModel by viewModels<LandingViewModel> {
        VMLandingFactory(
            LandingRepoImpl(
                LandingDataSource()
            )
        )
    }

 override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val sharedPref = requireContext().getSharedPreferences("LOCATION", Context.MODE_PRIVATE)
        val nombre = sharedPref.getString("name", null)
        location = name!!
    }

 override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        setupRecyclerView()
        fetchShops(location)
    }

 private fun fetchShops(localidad: String) {

        viewModel.setLocation(location.toLowerCase(Locale.ROOT).trim())
        viewModel.fetchShopList
            .observe(viewLifecycleOwner, Observer {

                when (it) {

                    is Resource.Loading -> {
                        showProgress()
                    }
                    is Resource.Success -> {
                        hideProgress()
                        myAdapter.setItems(it.data)
                    }
                    is Resource.Failure -> {
                        hideProgress()
                        Toast.makeText(
                            requireContext(),
                            "There was an error loading the shops.",
                            Toast.LENGTH_SHORT
                        ).show()
                    }
                }
            })

    }

Viewmodel

 private val locationQuery = MutableLiveData<String>()

    fun setLocation(location: String) {
        locationQuery.value = location
    }

    val fetchShopList = locationQuery.distinctUntilChanged().switchMap { location ->
        liveData(viewModelScope.coroutineContext + Dispatchers.IO) {
            emit(Resource.Loading())
            try{
                emit(repo.getShopList(location))
            }catch (e:Exception){
                emit(Resource.Failure(e))
            }
        }
        }

现在,如果我转到下一个片段并按回,则再次触发,我知道这可能是因为片段正在重新创建,然后传递新的viewmodel实例,这就是为什么不保留位置的原因,但是如果我将activityViewModels作为viewmodel的实例,它也发生相同的情况,再次在backpress上加载数据,这是不可接受的,因为每次返回都会获取数据,这对我来说服务器效率不高,我需要仅当用户位于此片段中并且如果他们按回以不再获取它时,才获取该数据。

任何线索?

我正在使用导航组件,所以我不能使用.add或执行片段事务,我想在首次创建该片段时只在该片段上获取一次,而不在下一个片段的backpress上重新获取

android android-fragments kotlin viewmodel android-architecture-components
1个回答
0
投票

TL; DR

即使UI重新订阅了它,您也需要使用仅发出一次事件的LiveData。有关更多信息和说明以及解决方法,请继续阅读。

当您从片段1进入->片段2时,片段1实际上并没有立即销毁,它只是从ViewModel LiveData取消订阅。

现在,当您从F2返回F1时,该片段将重新订阅到ViewModel LiveData,并且由于LiveData本质上是状态持有者,因此它将立即重新发射其最新值,从而导致ui重新绑定。

您需要的是某种LiveData,它不会发出以前发出的事件。

[这是LiveData的常见用例,有一篇不错的文章谈论了针对不同类型的用例的类似LiveData的需求,您可以阅读here

尽管本文提出了一些解决方案,但有时这些解决方案可能有点过头,因此使用以下ActionLiveView来实现更简单的解决方案>

// First extend the MutableLiveData class
class ActionLiveData<T> : MutableLiveData<T>() {

    @MainThread
    override fun observe(owner: LifecycleOwner, observer: Observer<T?>) {

       // Being strict about the observer numbers is up to you
       // I thought it made sense to only allow one to handle the event
        if (hasObservers()) {
            throw Throwable("Only one observer at a time may subscribe to a ActionLiveData")
        }

        super.observe(owner, Observer { data ->
            // We ignore any null values and early return
            if (data == null)  return
            observer.onChanged(data)
            // We set the value to null straight after emitting the change to the observer
            value = null
            // This means that the state of the data will always be null / non existent
            // It will only be available to the observer in its callback and since we do not emit null values
            // the observer never receives a null value and any observers resuming do not receive the last event.
            // Therefore it only emits to the observer the single action so you are free to show messages over and over again
            // Or launch an activity/dialog or anything that should only happen once per action / click :).
        })
    }

    // Just a nicely named method that wraps setting the value
    @MainThread
    fun sendAction(data: T) {
        value = data
    }
}

如果需要,您可以在this link中找到有关ActionLiveData的更多说明。

我建议使用ActionLiveData类,我已经将它用于中小型项目,到目前为止,它仍然可以正常工作,但是同样,您比我更了解用例。 :)

© www.soinside.com 2019 - 2024. All rights reserved.