4gu*_*71n 6 android mvvm android-livedata android-viewmodel
语境
所以,我一直在为几个项目使用 MVVM 架构。我仍在努力弄清楚并改进架构的工作方式。我总是使用 MVP 架构,使用常用的工具集,Dagger for DI,通常是多模块项目,Presenter 层被注入了一堆 Interactor/UseCases,每个 Interactor 被注入了不同的 Repositories 来执行后端 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)
}
}
Run Code Online (Sandbox Code Playgroud)
这里没有火箭科学,有时我会更改实现以使用多个 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)
}
)
}
}
Run Code Online (Sandbox Code Playgroud)
我的 Repository 类通常是 Retrofit 实例的包装器,它们负责设置适当的调度程序,以便一切都在适当的线程上运行,并将后端响应映射到模型类中。后端响应是指用 Gson 映射的类(例如 ApiProductResponse 列表),它们被映射到模型类(例如我在整个应用程序中使用的产品列表)
题
我的问题是,自从我开始使用 MVVM 架构所有文章和所有示例后,人们要么将 Repositories 直接注入 ViewModel(复制代码以处理错误并映射响应),要么使用 Single Source of Truth模式(使用 Room 的 Flowables 从 Room 获取信息)。但是我还没有看到有人使用带有 ViewModel 层的 UseCases。我的意思是它非常方便,我可以将事情分开,我在用例中映射后端响应,我处理那里的任何错误。但是,仍然觉得很可能我没有看到有人这样做,有什么方法可以改进用例,使它们在 API 方面对 ViewModel 更友好?使用回调接口以外的其他方式执行用例和视图模型之间的通信?
如果您需要有关此的更多信息,请告诉我。对不起,这些例子,我知道这些不是最好的,我只是想出了一些简单的东西来更好地解释它。
谢谢,
编辑 #1
这是我的存储库类的样子:
// 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
Run Code Online (Sandbox Code Playgroud)
更新您的用例以使其返回Single<List<Product>>:
class GetProducts @Inject constructor(private val repository: ApiProductRepository) {
operator fun invoke(): Single<List<Product>> {
return repository.fetchProducts()
}
}
Run Code Online (Sandbox Code Playgroud)
然后,更新您的ViewModel,以便它订阅产品流:
class ProductDetailViewModel @Inject constructor(
private val getProducts: GetProducts
): ViewModel() {
val state: LiveData<ProductDetailViewState> get() = _state
private val _state = MutableLiveData<ProductDetailViewState>()
private val compositeDisposable = CompositeDisposable()
init {
subscribeToProducts()
}
override fun onCleared() {
super.onCleared()
compositeDisposable.clear()
}
private fun subscribeToProducts() {
getProducts()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.main())
.subscribe(
{
// onNext()
_state.value = ProductListFetched(products = it)
},
{
// onError()
_state.value = ErrorFetchingInfo
}
).addTo(compositeDisposable)
}
}
sealed class ProductDetailViewState {
data class ProductListFetched(
val products: List<Product>
): ProductDetailViewState()
object ErrorFetchingInfo : ProductDetailViewState()
}
Run Code Online (Sandbox Code Playgroud)
List<ApiProductResponse>>我忽略的一件事是to的适应List<Product>,但这可以通过使用辅助函数映射列表来处理。
| 归档时间: |
|
| 查看次数: |
3387 次 |
| 最近记录: |