我有一个片段,我想对其数据进行一次提取,我只使用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()
}
}
})
}
视图模型
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))
}
}
}
现在,如果我转到下一个片段并按回,这会再次触发,我知道这可能是因为片段正在重新创建然后传递视图模型的新实例,这就是为什么没有保留该位置的原因,但是如果我activityViewModels
作为视图模型的实例,它也会发生同样的情况, 数据在 backpress 上再次加载,这是不可接受的,因为每次返回都会获取数据,这对我来说不是服务器效率的,当用户在此片段中时,我只需要获取这些数据,如果他们按回去不再获取它。
有什么线索吗?
我正在使用导航组件,所以我不能使用 .add 或进行片段事务,我想在第一次创建时只在这个片段上获取一次,而不是在下一个片段的反压时重新获取
TL;博士
您需要使用仅发出一次其事件的 LiveData,即使 UI 重新订阅它也是如此。 有关更多信息和说明以及解决方法,请继续阅读。
当您从片段 1 转到片段 2>片段 2 时,片段 1 实际上并没有立即销毁,它只是取消订阅您的 ViewModel LiveData。
现在,当您从 F2 返回到 F1 时,片段将重新订阅回 ViewModel LiveData,并且由于 LiveData 本质上是状态持有者,因此它将立即重新发出其最新值,从而导致 UI 重新绑定。
你需要的是某种不会发出以前发出的事件的 LiveData。
这是 LiveData的常见用例,有一篇非常好的文章讨论了针对不同类型的用例需要类似的 LiveData,您可以在此处阅读。
尽管本文提出了一些解决方案,但有时这些解决方案可能有点矫枉过正,因此更简单的解决方案是使用以下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
}
}
如果需要,您可以在此链接中找到有关ActionLiveData的更多解释。
我建议使用 ActionLiveData 类,我一直在将其用于中小型项目,到目前为止它工作正常,但同样,您比我更了解您的用例。 :)