在Android单向数据流中更新不可变视图状态值 问题:

在Android单向数据流中更新不可变视图状态值 问题:,android,kotlin,android-livedata,android-viewmodel,Android,Kotlin,Android Livedata,Android Viewmodel,我希望在Android ViewModel(VM)中重构不可变视图状态的值,以便执行以下操作: 干净地更新VM中的视图状态,而不复制整个视图状态 保持视图状态数据不受视图更新的影响 我已经构建了一个Android单向数据流(UDF)模式,使用LiveData更新在视图中观察到的VM中的视图状态更改 见: 完整示例代码: 实施 现有的实现使用嵌套的LiveData 一个LiveDataval将视图状态存储在VM中 视图状态属性的嵌套LiveData为不可变vals 我不确定使用“嵌套的Live

我希望在Android ViewModel(VM)中重构不可变视图状态的值,以便执行以下操作:

  • 干净地更新VM中的视图状态,而不复制整个视图状态
  • 保持视图状态数据不受视图更新的影响
  • 我已经构建了一个Android单向数据流(UDF)模式,使用LiveData更新在视图中观察到的VM中的视图状态更改

    见:

    完整示例代码:

    实施 现有的实现使用嵌套的LiveData

    • 一个LiveData
      val
      将视图状态存储在VM中
    • 视图状态属性的嵌套LiveData为不可变
      val
      s
    我不确定使用“嵌套的LiveData”是否可以。当我们使用任何事件驱动的设计实现(
    LiveData
    RxJava
    Flow
    )时,我们通常需要假设离散数据事件是不可变的,对这些事件的操作是纯功能性的。不可变并不等同于只读(
    val
    )。不可变的意思是不可变的。它应该是时不变的,并且在任何情况下都应该以完全相同的方式工作。这就是为什么我对数据类中有
    LiveData
    ArrayList
    成员感到奇怪的原因之一,不管它们是否定义为只读

    避免嵌套流的另一个技术原因是:几乎不可能正确观察它们。每当有新的数据事件通过外部流发出时,开发人员必须确保在观察新的内部流之前删除内部订阅,否则它可能会导致各种问题。当开发人员需要手动取消订阅时,拥有生命周期感知的观察者有什么意义

    在几乎所有场景中,嵌套流都可以转换为一层流。就你而言:

    class ViewModel: ViewModel() {
    
        val contentList: LiveData<PagedList<Content>>
        val anotherAttribute: LiveData<Int>
    
        private val swipeToRefreshTrigger = MutableLiveData<Boolean>(true)
    
        init {
            contentList = Transformations.switchMap(swipeToRefreshTrigger) {
                getContentList(...)
            }
    
            anotherAttribute = ...
        }
    
        override fun swipeToRefresh(event: SwipeToRefresh) {
            swipeToRefreshTrigger.postValue(true)
        }
    }
    
    class-ViewModel:ViewModel(){
    val contentList:LiveData
    val另一个属性:LiveData
    private val swipeToRefreshTrigger=MutableLiveData(true)
    初始化{
    contentList=Transformations.switchMap(swipeToRefreshTrigger){
    getContentList(…)
    }
    另一个属性=。。。
    }
    覆盖有趣的swipeToRefresh(事件:swipeToRefresh){
    swipeToRefreshTrigger.postValue(真)
    }
    }
    
    页面列表上的注释:

    PagedList
    也是可变的,但我想这是我们不得不忍受的<代码>页面列表使用是另一个话题,所以我不会在这里讨论。

    使用Kotlin StateFlow-7/21/20更新 通过Kotlin协程,可以在ViewModel中更新StateFlow值,并通过接口方法在视图的activity/fragment中呈现,而不是具有LiveData的两个状态类,一个是私有的和可变的,另一个是公共的和不可变的

    见:

    删除嵌套的LiveData,创建状态类-2/11/20 方法:将不可变的LiveData状态和效果存储在可公开访问的ViewModel内的视图状态和视图效果类中

    视图状态和视图效果属性可以直接在VM中为LiveData值。但是,我想将视图状态和效果组织到单独的类中,以便让视图知道它是观察视图状态还是观察视图效果

    class FeedViewState(
            _contentList: MutableLiveData<PagedList<Content>>,
            _anotherAttribute: MutableLiveData<Int>
    ) {
        val contentList: LiveData<PagedList<Content>> = _contentList
        val anotherAttribute: LiveData<Int> = _anotherAttribute
    }
    
    类FeedViewState(
    _contentList:MutableLiveData,
    _另一个属性:MutableLiveData
    ) {
    val contentList:LiveData=\u contentList
    val anotherAttribute:LiveData=\u anotherAttribute
    }
    
    视图状态是在VM中创建的

    class ViewModel: ViewModel() {
    
        val feedViewState: FeedViewState 
        private val _contentList = MutableLiveData<PagedList<Content>>()
        private val _anotherAttribute = MutableLiveData<Int>()
    
        init {
            feedViewState = FeedViewState(_contentList, _anotherAttribute)
        }
    
        ...
    
        fun updateContent(){
            _contentList.value = ...
        }
    
        fun updateAnotherAttribute(){
            _anotherAttribute.value = ...
        }
    }
    
    class-ViewModel:ViewModel(){
    val feedViewState:feedViewState
    private val_contentList=MutableLiveData()
    private val_anotherAttribute=MutableLiveData()
    初始化{
    feedViewState=feedViewState(\u内容列表,\u其他属性)
    }
    ...
    乐趣更新内容(){
    _contentList.value=。。。
    }
    fun updateAnotherAttribute(){
    _anotherAttribute.value=。。。
    }
    }
    
    然后,将在活动/片段中观察视图状态属性

    class Fragment: Fragment() {
        private fun observeViewState() {
            feedViewModel.feedViewState.contentList(viewLifecycleOwner){ pagedList: PagedList<Content> ->
                adapter.submitList(pagedList)
            }
            feedViewModel.feedViewState.anotherAttribute(viewLifecycleOwner){ anotherAttribute: Int ->
                //TODO: Do something with other attribute.
            }
        }
    }
    
    类片段:片段(){
    私人娱乐观察网站(){
    feedViewModel.feedViewState.contentList(viewLifecycleOwner){pagedList:pagedList->
    适配器提交列表(页面列表)
    }
    feedViewModel.feedViewState.anotherAttribute(viewLifecycleOwner){anotherAttribute:Int->
    //TODO:使用其他属性执行某些操作。
    }
    }
    }
    
    感谢您的反馈@SanlokLee!拥有嵌套的LiveData流在代码功能方面并没有很大的好处。在你看来,这可能会导致意外事件。实现此模式的最初原因是将视图状态和视图效果组织到它们自己的类中,以便在视图中清楚地看到所观察到的内容。我将重构它并分享上面的新模式。看看上面的解决方案。视图状态LiveData属性存储在它们自己的类中,而类本身不是LiveData对象。这简化了值的设置和存储方式。我期待着您的反馈!如果你认为问答对其他人有用,请投票表决。
    class ViewModel: ViewModel() {
    
        val feedViewState: FeedViewState 
        private val _contentList = MutableLiveData<PagedList<Content>>()
        private val _anotherAttribute = MutableLiveData<Int>()
    
        init {
            feedViewState = FeedViewState(_contentList, _anotherAttribute)
        }
    
        ...
    
        fun updateContent(){
            _contentList.value = ...
        }
    
        fun updateAnotherAttribute(){
            _anotherAttribute.value = ...
        }
    }
    
    class Fragment: Fragment() {
        private fun observeViewState() {
            feedViewModel.feedViewState.contentList(viewLifecycleOwner){ pagedList: PagedList<Content> ->
                adapter.submitList(pagedList)
            }
            feedViewModel.feedViewState.anotherAttribute(viewLifecycleOwner){ anotherAttribute: Int ->
                //TODO: Do something with other attribute.
            }
        }
    }