考虑一个显示数据列表并让用户向下钻取的Android应用程序。在某些时候,用户对需要反映在后退堆栈上的多个Activity 中的数据进行更改。实现这一目标的最佳模式是什么?
首先,让我们检查一下案例。
应用中有一个数据源。并且它的更新需要反映到多个UI,
- 情况 1:某些 UI 可能只需要整个数据源
- 情况 2:某些 UI 需要先处理数据,然后才能显示在 UI 上
您需要以某种方式为所有 UI 创建单一事实来源,并在发生某些事情时反映对它们的更改。
我通过使用 google 新架构组件中的MVVM
和ViewModel
+LiveData
在我的项目中解决了这个问题。为什么?因为他们有生命周期意识!你可以使用RxJava来做同样的事情。
1.model layer
它是一个单一实例,并将数据源公开为LiveData
A。在下面的代码中,它将OrderLiveStore.liveData
。
class OrderLiveStore(
private val orderStore: OrderStore
) {
var liveData: MutableLiveData<List<Order>> = MutableLiveData()
init {
liveData.value = orderStore.items
}
}
2.view model layer
:
情况 1:您只需将该模型注入视图模型,然后将
LiveData
A 公开给视图。在考虑单一实例的事实之下,连接到此视图模型的所有视图都将获得更新,因为视图模型只是从单一实例变量返回相同的属性。我通过使用dagger
来管理单例。class OrdersViewModel @Inject constructor( orderLiveStore: OrderLiveStore ): ViewModel() { // expose to the view directly val orders: LiveData<List<Order>> = orderLiveStore.liveData }
情况 2:您仍然将模型注入视图模型,但在内部,您需要使用
Transformations.map
订阅它,并进行处理,并将结果公开给视图层class OrderViewModel( orderLiveStore: OrderLiveStore, private val orderId: String ) : ViewModel() { // expose to the view after processing it val order: LiveData<Order> = Transformations.map(orderLiveStore.liveData) { getNeededOrderFromList(it) } private fun getNeededOrderFromList(orderList: List<Order>?): Order? { // This method will be triggered every time orderStore.liveData gets updated } }
你可以看到,在案例1中,我使用dagger注射,因为它适合这种情况。在案例 2 中,我使用自定义参数在视图中创建了视图模型,因为视图模型需要一些额外的信息才能从模型层中获取所需的部分。就我而言,这是一个orderId:String
3.view layer
现在很简单,无论是片段还是活动,您观察该数据源并更新您的 UI,
orderViewModel.orders.observe(this, Observer {
// update the ui
})
或者更优雅地说,如果您不需要太多的预处理,则可以使用数据绑定将视图模型中的LiveData
直接绑定到xml
。
4. 克鲁德呢
好吧,您只需直接更新model
层。但是操作将从 2 层之一开始
view
图层(如果来自用户)view model
层(如果是副作用)。
但即使它来自view
,view
仍然会在view model
上调用方法,view model
会在模型层上调用一些方法,或者你可以简单地在视图模型层中更新它取决于情况(因为你得到了单一的事实来源)。
这有点像 Redux 模式 - 一种几乎单向的数据流,其中每个更改都将发生在模型层并反射回视图模型层,然后冒泡到视图层。很容易推断数据流。
5.结果将是你想要的。
因为现在一切都连接到单一事实来源(共享模型层),但以解耦的方式。每一层都有自己的工作。
6. 再提一个提示
为了使Transformations.map
正常工作,您需要在视图中observe
结果,否则,来自Transformations.map
的订阅将根本不起作用。
您可以将共享数据放入服务中,然后在片段/活动的onResume
方法中,您可以从那里获取更新的数据。
要更新当前片段/活动,您可以在更新服务中的数据时触发事件,注册片段/活动以捕获它,从而更新显示的数据。您可以使用OttoBus来实现这一目标