具有Interactors / Use Cases的MVVM架构

问题描述 投票:0回答:1

上下文

所以,我一直在使用MVVM架构只是为了几个项目。我仍在努力弄清楚并改进架构的工作原理。我一直使用MVP架构,使用通常的工具集,Dagger for DI,通常是多模块项目,Presenter层注入了一堆Interactors / UseCases,每个Interactor注入了不同的存储库来执行后端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层。

我的UseCases通常如下所示:

// 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实例的包装器,它们负责设置正确的Scheduler,所以一切都在正确的线程上运行,并将后端响应映射到模型类。通过后端响应,我指的是用Gson映射的类(例如ApiProductResponse列表),它们被映射到模型类(例如我在App中使用的产品列表)

我的问题是,自从我开始使用MVVM架构所有文章和所有示例以来,人们要么将存储库注入ViewModel(复制代码以处理错误并映射响应),要么使用单一真实来源pattern(使用Room的Flowables从Room获取信息)。但我没有看到任何人将UseCases与ViewModel层一起使用。我的意思是它非常方便,我将事物分开,我在UseCases中进行后端响应的映射,我处理任何错误。但是,我觉得我没有看到有人这样做的可能性,是否有一些方法可以改进UseCases以使它们在API方面对ViewModel更友好?使用除回调接口之外的其他东西来执行UseCase和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
android mvvm android-livedata android-viewmodel
1个回答
0
投票

我刚刚开始在我的项目的最后两个项目中使用MVVM。我可以与您分享我在ViewModel中处理REST API的过程。希望它能帮助你和他人。

  • 使用回调创建一个通用的Retrofit Executer类。它将采用改装调用对象并为您提供数据。
  • 为您可以处理所有API请求的特定包或模块创建存储库。在我的情况下,我从API获取其id的一个用户。这是User Repository。

class UserRepository {


    @Inject
    lateinit var mRetrofit: Retrofit

    init {
        MainApplication.appComponent!!.inject(this)
    }

    private val userApi = mRetrofit.create(UserApi::class.java)

    fun getUserbyId(id: Int): Single<NetworkResponse<User>> {
        return Single.create<NetworkResponse<User>>{
            emitter ->
            val callbyId = userApi.getUserbyId(id)
            GenericReqExecutor(callbyId).executeCallRequest(object : ExecutionListener<User>{
                override fun onSuccess(response: User) {
                    emitter.onSuccess(NetworkResponse(success = true,
                            response = response
                            ))
                }

                override fun onApiError(error: NetworkError) {
                    emitter.onSuccess(NetworkResponse(success = false,
                            response = User(),
                            networkError = error
                            ))
                }

                override fun onFailure(error: Throwable) {
                    emitter.onError(error)
                }

            })
        }
    }

}
  • 然后在ViewModel中使用此存储库。在我的情况下,这是我的LoginViewModel代码。

 class LoginViewModel : ViewModel()  {

     var userRepo = UserRepository()

     fun getUserById(id :Int){
         var diposable = userRepo.getUserbyId(id).subscribe({

             //OnNext

         },{
             //onError
         })
     }
}

我希望这种方法可以帮助您减少一些样板代码。谢谢

© www.soinside.com 2019 - 2024. All rights reserved.