首页 > 解决方案 > 在 Android 中制作有状态组件

问题描述

我在我的应用程序中使用 MVVM。当您输入查询并单击搜索按钮时,链如下:Fragment -> ViewModel -> Repository -> API -> Client。客户端是发出 HTTP 请求的地方。但是这里有一件事,客户端需要在初始化时进行调用并从服务器获取密钥。因此,为了防止在第一次调用完成之前进行任何调用,我需要能够从 Fragment 观察它,以便我可以禁用搜索按钮。由于链中的每个组件都可以与相邻的组件通信,因此所有组件都应该有一个状态。

我正在考虑实现一个StatefulComponent类并制作所有组件来扩展它:

open class StatefulComponent protected constructor() {
    enum class State {
        CREATED, LOADING, LOADED, FAILED
    }

    private val currentState = MutableLiveData(State.CREATED)

    fun setState(newState: State) {
        currentState.value = newState
    }

    val state: LiveData<State> = currentState

    val isLoaded: Boolean = currentState.value == State.LOADED

    val isFailed: Boolean = currentState.value == State.FAILED

    val isCompleted: Boolean = isLoaded || isFailed
}

这个想法是每个组件观察下一个并相应地更新自己。然而,这对 ViewModel 来说是不可能的,因为它已经在扩展ViewModel超类。

我该如何解决这个问题?

标签: androidandroid-mvvm

解决方案


最常见的方法是使用密封类作为您的状态,因此您可以根据需要在每个状态案例中使用任何参数。

sealed class MyState {
   object Loading : MyState()
   data class Loaded(data: Data) : MyState()
   data class Failed(message: String) : MyState()
}

在您的视图模型上,您将只有 1 个实时数据

class MyViewModel : ViewModel() {
    private val _state = MutableLiveData<MyState>()
    val state: LiveData<MyState> = _state

    fun load() {
       _state.postCall(Loading)
       repo.loadSomeData(onData = { data ->
           _state.postCall(Loaded(data))
       }, onError = { error -> _state.postCall(Failed(error.message)) })
    }

    // coroutines approach
    suspend fun loadSuspend() {
      _state.postCall(Loading)
      try {
        _state.postCall(Loaded(repo.loadSomeDataSupend()))
      } catch(e: Exception) {
        _state.postCall(Failed(e.message))
      }
    }
}

而在片段上,只需观察状态

class MyFragment : Fragment() {
   ...
   onViewCreated() {
     viewModel.state.observer(Observer {
         when (state) {
          // auto casts to each state
          Loading -> { button.isEnabled = false }
          is Loaded -> { ... }
          is Failed -> { ... }
         }
       }
     )
   }
}

推荐阅读