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

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在Android单向数据流中更新不可变视图状态值相关的知识,希望对你有一定的参考价值。

问题

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

  1. 干净地更新VM中的视图状态,而无需复制整个视图状态
  2. 保持视图状态数据对视图更新不可变

我使用LiveData构建了android单向数据流(UDF)模式,以更新视图中观察到的VM中的视图状态更改。

参见:Android Unidirectional Data Flow with LiveData — 2.0

完整示例代码:Coinverse Open App

实施

现有实现使用嵌套的LiveData。

  • 一个LiveData val将视图状态存储在VM中
  • 视图状态属性的嵌套LiveData为不可变的val s
// Stored as viewState LiveData val in VM
data class FeedViewState(
    val contentList: LiveData<PagedList<Content>>
    val anotherAttribute: LiveData<Int>)

视图状态在VM的init...中创建。

然后,为了更新视图状态,必须将其复制并使用给定的属性进行更新,因为它是不可变的val。如果该属性是可变的,则可以在VM中没有copy的情况下重新分配该属性。但是,保持不变是很重要的,以确保视图不会无意间更改val

class ViewModel: ViewModel() 
    val viewState: LiveData<FeedViewState> get() = _viewState
    private val _viewState = MutableLiveData<FeedViewState>()

    init 
        _viewState.value = FeedViewState(
           contentList = getContentList(...)
           anotherAttribute = ...)
    

    override fun swipeToRefresh(event: SwipeToRefresh) 
        _viewState.value = _viewState.value?.copy(contentList = getContentList(...))
    

尝试的解决方案

Approach:删除视图状态和视图状态属性的LiveData嵌套,同时将视图状态数据保持在单独的类中。

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

为了保持视图状态井井有条,我想创建一个非LiveData视图类,其中包含LiveData属性。不幸的是,此模式无法与错误一起编译。

类型为<< [type的

表达式'名称'不能作为函数调用。找不到函数invoke()

class FeedViewState2(_contentList: MutableLiveData<PagedList<Content>>, _anotherAttribute: MutableLiveData<Int>) val contentList: LiveData<PagedList<Content>> = _contentList val anotherAttribute: LiveData<Int> = _anotherAttribute
视图状态在VM中创建。

class ViewModel: ViewModel() val feedViewState2: FeedViewState2 private val _contentList = MutableLiveData<PagedList<Content>>() private val _anotherAttribute = MutableLiveData<Int>() init feedViewState2 = FeedViewState2(_contentList, _anotherAttribute)

然后,将在活动/片段中观察视图状态属性。

class Fragment: Fragment() private fun observeViewState() feedViewModel.feedViewState2.contentList(viewLifecycleOwner) pagedList: PagedList<Content> -> adapter.submitList(pagedList) feedViewModel.feedViewState2.anotherAttribute(viewLifecycleOwner) anotherAttribute: Int -> //TODO: Do something with other attribute.

答案
我不确定是否有“嵌套LiveData”是否可以。当我们使用任何事件驱动的设计实现(LiveDataRxJavaFlow)时,通常需要假定离散数据事件是不可变的,并且对这些事件的操作是纯功能的。不变是不是只读(val)的同义词。不可变意味着不可变。它应该是时不变的,并且在任何情况下都应该以完全相同的方式工作。这就是为什么我对在数据类中具有LiveDataArrayList成员感到奇怪的原因之一,无论它们是否被定义为只读。

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

几乎在所有情况下,嵌套流都可以转换为一层流。您的情况:

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)

PagedList的注意事项:

PagedList也是可变的,但是我想这是我们必须忍受的。 PagedList的使用是另一个主题,因此在这里我不再讨论。

以上是关于在Android单向数据流中更新不可变视图状态值的主要内容,如果未能解决你的问题,请参考以下文章

不可变状态更新。在 Redux 中更新对象数组

更新深层不可变状态属性时,Redux 不更新组件

我想以不可变的方式更新状态对象,以便react和redux正常工作?

番外篇-Flutter初识三问

Android - MVVM中ViewModel状态的最佳实践?

怎样理解 Vue 的单向数据流?