【发布时间】:2019-03-30 14:29:30
【问题描述】:
上下文
所以,我一直在为几个项目使用 MVVM 架构。我仍在尝试弄清楚并改进架构的工作方式。我一直使用 MVP 架构,使用通常的工具集,用于 DI 的 Dagger,通常是多模块项目,Presenter 层被注入一堆交互器/用例,每个交互器被注入不同的存储库以执行后端 API 调用.
现在我已经进入 MVVM,我通过 ViewModel 更改了 Presenter 层,从 ViewModel 到 UI 层的通信是通过 LiveData 完成的,而不是使用 View 回调接口,等等。
看起来像这样:
class ProductDetailViewModel @inject constructor(
private val getProductsUseCase: GetProductsUseCase,
private val getUserInfoUseCase: GetUserInfoUseCase,
) : ViewModel(), GetProductsUseCase.Callback, GetUserInfoUseCase.Callback {
// Sealed class used to represent the state of the ViewModel
sealed class ProductDetailViewState {
data class UserInfoFetched(
val userInfo: UserInfo
) : ProductDetailViewState(),
data class ProductListFetched(
val products: List<Product>
) : ProductDetailViewState(),
object ErrorFetchingInfo : ProductDetailViewState()
object LoadingInfo : ProductDetailViewState()
}
...
// Live data to communicate back with the UI layer
val state = MutableLiveData<ProductDetailViewState>()
...
// region Implementation of the UseCases callbacks
override fun onSuccessfullyFetchedProducts(products: List<Product>) {
state.value = ProductDetailViewState.ProductListFetched(products)
}
override fun onErrorFetchingProducts(e: Exception) {
state.value = ProductDetailViewState.ErrorFetchingInfo
}
override fun onSuccessfullyFetchedUserInfo(userInfo: UserInfo) {
state.value = ProductDetailViewState.UserInfoFetched(userInfo)
}
override fun onErrorFetchingUserInfo(e: Exception) {
state.value = ProductDetailViewState.ErrorFetchingInfo
}
// Functions to call the UseCases from the UI layer
fun fetchUserProductInfo() {
state.value = ProductDetailViewState.LoadingInfo
getProductsUseCase.execute(this)
getUserInfoUseCase.execute(this)
}
}
这里没有火箭科学,有时我会更改实现以使用多个 LiveData 属性来跟踪更改。顺便说一句,这只是我写的一个例子,所以不要指望它编译。但就是这样,ViewModel 被注入了一堆 UseCases,它实现了 UseCases 回调接口,当我从 UseCases 获得结果时,我通过 LiveData 将其传达给 UI 层。
我的用例通常如下所示:
// UseCase interface
interface GetProductsUseCase {
interface Callback {
fun onSuccessfullyFetchedProducts(products: List<Product>)
fun onErrorFetchingProducts(e: Exception)
}
fun execute(callback: Callback)
}
// Actual implementation
class GetProductsUseCaseImpl(
private val productRepository: ApiProductRepostory
) : GetProductsUseCase {
override fun execute(callback: Callback) {
productRepository.fetchProducts() // Fetches the products from the backend through Retrofit
.subscribe(
{
// onNext()
callback.onSuccessfullyFetchedProducts(it)
},
{
// onError()
callback.onErrorFetchingProducts(it)
}
)
}
}
我的 Repository 类通常是 Retrofit 实例的包装器,它们负责设置正确的调度程序,以便一切都在正确的线程上运行并将后端响应映射到模型类中。通过后端响应,我的意思是用 Gson 映射的类(例如 ApiProductResponse 列表)并将它们映射到模型类(例如我在整个应用程序中使用的产品列表)
问题
我的问题是,自从我开始使用所有文章和所有示例的 MVVM 架构以来,人们要么将存储库直接注入 ViewModel(复制代码以处理错误并映射响应),要么使用 Single Source of Truth 模式(使用 Room 的 Flowables 从 Room 获取信息)。但我还没有看到有人使用带有 ViewModel 层的 UseCases。我的意思是它非常方便,我可以把事情分开,我在用例中映射后端响应,我在那里处理任何错误。但是,我仍然觉得我没有看到有人这样做,有没有办法改进 UseCases 以使其在 API 方面对 ViewModels 更友好?使用回调接口以外的其他方式执行 UseCases 和 ViewModel 之间的通信?
如果您需要更多关于此的信息,请告诉我。抱歉这些例子,我知道这些不是最好的,我只是想出了一些简单的东西来更好地解释它。
谢谢,
编辑#1
这就是我的 Repository 类的样子:
// ApiProductRepository interface
interface ApiProductRepository {
fun fetchProducts(): Single<NetworkResponse<List<ApiProductResponse>>>
}
// Actual implementation
class ApiProductRepositoryImpl(
private val retrofitApi: ApiProducts, // This is a Retrofit API interface
private val uiScheduler: Scheduler, // AndroidSchedulers.mainThread()
private val backgroundScheduler: Scheduler, // Schedulers.io()
) : GetProductsUseCase {
override fun fetchProducts(): Single<NetworkResponse<List<ApiProductResponse>>> {
return retrofitApi.fetchProducts() // Does the API call using the Retrofit interface. I've the RxAdapter set.
.wrapOnNetworkResponse() // Extended function that converts the Retrofit's Response object into a NetworkResponse class
.observeOn(uiScheduler)
.subscribeOn(backgroundScheduler)
}
}
// The network response class is a class that just carries the Retrofit's Response class status code
【问题讨论】:
-
你找到正确的解决方案了吗?
-
您找到不使用 UseCase/Interactor 的理由/案例了吗?我从 MVVM 方法开始,我不确定它们是否是不必要的层。
-
@Abbas 不,实际上我在最新的 MVVM 项目中使用了用例。唯一的区别是,我使用 RxJava,而不是使用回调将用例传递给 ViewModel。
-
@4gus71 你的意思是使用 RxJava 和 observsbles 吗?所以在 ViewModel 中我们订阅 observable 变量,将其传递给 Interactor 方法,并通过更新 Interactor 中的 observable 变量来更新 ViewModel 中的值。如果我不正确,请纠正我。
-
@IgorLevkivskiy 实际上在我的交互器上,我只有返回 Observable 的函数。在 ViewModel 我调用这个函数,订阅 observable 并做我需要做的任何事情。例如,获取订单列表的交互器将有一个函数:
fun fetchOrders(): Observable<List<Order>> {...然后在 ViewModel 上我做:fetchOrdersInteractor.fetchOrders().subscribe {....}类似的东西。
标签: android mvvm android-livedata android-viewmodel