Android 如何在使用ViewModel重新创建片段时执行一次性提取操作

Android 如何在使用ViewModel重新创建片段时执行一次性提取操作,android,android-fragments,kotlin,mvvm,android-architecture-navigation,Android,Android Fragments,Kotlin,Mvvm,Android Architecture Navigation,我进入FragmentA(),进入FragmentA()后,我将数据提取到我的服务器,当这些数据到来时,我填充一个列表 现在,如果我从FragmentA()转到FragmentB()并从FragmentB()按下后退按钮或导航回FragmentA(),它会将列表重新写入服务器并重新填充列表 我不希望这样,相反,我希望我的viewmodel方法不再触发,我正在寻求此帮助,因为导航组件不允许我执行.add操作来保存我的片段a()的状态 当我每次从FragmentB()到FragmentA()进行反压

我进入FragmentA(),进入FragmentA()后,我将数据提取到我的服务器,当这些数据到来时,我填充一个列表

现在,如果我从FragmentA()转到FragmentB()并从FragmentB()按下后退按钮或导航回FragmentA(),它会将列表重新写入服务器并重新填充列表

我不希望这样,相反,我希望我的viewmodel方法不再触发,我正在寻求此帮助,因为导航组件不允许我执行.add操作来保存我的片段a()的状态

当我每次从FragmentB()到FragmentA()进行反压时,是否可以将此作为一次获取操作来执行,而不是重新蚀刻

碎片a()
private val viewModel by viewModels{
登陆工厂(
着陆报告(
LandingDataSource()
)
)
}
重写创建时的乐趣(savedInstanceState:Bundle?){
super.onCreate(savedInstanceState)
val sharedPref=requireContext().getSharedReferences(“位置”,Context.MODE\u PRIVATE)
val nombre=sharedPref.getString(“名称”,null)
位置=名称!!
}
覆盖已创建的视图(视图:视图,保存状态:捆绑?){
super.onViewCreated(视图,savedInstanceState)
setupRecyclerView()
商店(地点)
}
私人娱乐商店(localidad:String){
viewModel.setLocation(location.toLowerCase(Locale.ROOT.trim())
viewModel.fetchShopList
.观察(视图生命周期所有者、观察者{
什么时候{
正在加载资源->{
showProgress()
}
是资源。成功->{
hideProgress()
myAdapter.setItems(it.data)
}
是资源。失败->{
hideProgress()
Toast.makeText(
requireContext(),
“加载商店时出错。”,
吐司长度
).show()
}
}
})
}
视图模型
private val locationQuery=MutableLiveData()
乐趣设置位置(位置:字符串){
locationQuery.value=位置
}
fun fetchShopList(shopId:String)=locationQuery.distinctUntilChanged().switchMap{location->
liveData(viewModelScope.coroutineContext+Dispatchers.IO){
发出(Resource.Loading())
试一试{
发出(repo.getShopList(shopId,位置))
}捕获(e:例外){
发射(资源故障(e))
}
}
}
如何在FragmentA()中只提取一次,并将这些值保留在viewmodel中,然后在尝试重新提取时不再重复

我急切地想知道我怎样才能使这项工作成功


感谢

全能的谷歌在其

    fun setId(owner: String, name: String) {
        val update = RepoId(owner, name)
        if (_repoId.value == update) {
            return
        }
        _repoId.value = update
    }
这样,即使在重新创建片段时,只要viewmodel处于活动状态,您也不会从服务器请求新数据,但livedata将再次发送其最新数据,以便片段可以更新其视图


另外,您是否认为locationQuery.distinctUntilChanged()不起作用?您是否正在监视是否有请求被发送到您的服务器?

多亏了Henrique,我以一种更简单的方式解决了这个问题

private var shouldFetchAgain = true

    fun fetchShopList(shopId: String, location: String) = liveData(Dispatchers.IO) {

        if (shouldFetchAgain) {
            emit(Resource.Loading())
            try {
                emit(repo.getShopList(shopId, location))
                shouldFetchAgain = false
            } catch (e: Exception) {
                emit(Resource.Failure(e))
            }
        }
    }

我知道这是不安全的,因为我无法将多个观察者添加到这一点上,但对于我的用例,我只有一个观察者来观察此数据

不要在
onViewCreated
中调用
fetchShops
。相反,只在ViewModel的
init
块中调用它,将数据缓存在ViewModel的字段中,并在
onViewCreated
中观察该值。只要您的ViewModel处于活动状态(当片段A位于后台时应该是这种情况),它就不会再次运行
init
块。

片段可以使用活动范围并共享一个ViewModel,如所述
fetchShopList
变量,如果我理解正确,将运行一次,因此,在实例化ViewModel时,它将根据定义只获取一次。但是,在弹出FragmentA()时,如何清除只有ViewModel?因为如果我不能清除它,viewmodel将一直处于活动状态,直到我杀死包含该片段的活动@Bömachtblau有一篇关于如何是的帖子,但这会清除所有viewmodel,不仅仅是一个,但是viewmodel实例将在片段被销毁时死亡,对吗?因为如果在从片段A到片段B的过程中发生这种情况,viewmodel将被销毁。如果只是简单地从A导航到B,则不会被销毁,片段A将被保留在后台。您可以在代码中使用一些日志轻松地进行尝试,以查看它是否再次请求。因此,当我现在将其反按到FragmentA()中时,它将从Backback中弹出,并且viewmodel将被清除,对吗?我在该链接中看到了有关AbsentLiveData的内容,该数据类不是正常的数据类,对吗?当您按Back时,viewmodel将与以前相同,保留其以前保存的状态/数据。因此,您的片段将重新创建其视图,再次请求数据,livedata将提供,而无需向服务器请求。
    fun setId(owner: String, name: String) {
        val update = RepoId(owner, name)
        if (_repoId.value == update) {
            return
        }
        _repoId.value = update
    }
private var shouldFetchAgain = true

    fun fetchShopList(shopId: String, location: String) = liveData(Dispatchers.IO) {

        if (shouldFetchAgain) {
            emit(Resource.Loading())
            try {
                emit(repo.getShopList(shopId, location))
                shouldFetchAgain = false
            } catch (e: Exception) {
                emit(Resource.Failure(e))
            }
        }
    }