Android学习之ViewModel与Repository的关

2022-03-02  本文已影响0人  丶淡恋小情绪x

该系列文章为自学过程中的产出,若有错误,希望热心路人不吝赐教


之前的文章Android学习之MVVM简谈过MVVM,这章着重讲一下如何在Android的代码中表现ViewModel

Responsibility(职责)

既然View只管视图相关的逻辑,Model只管数据与规范,那ViewModel就注定要做最脏最累的活了(名字长的代价)。
网络调用谁做?ViewModel做!
IO谁做?ViewModel做!
保存分页参数谁做?ViewModel做!
1+1=?谁做?还是ViewModel做!

ViewModel code

贴一段Google的sample code:

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.paging.PagingData
import androidx.paging.cachedIn
import com.google.samples.apps.sunflower.data.UnsplashPhoto
import com.google.samples.apps.sunflower.data.UnsplashRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject

@HiltViewModel // hilt依赖,修改这个class,告诉编译器,这个class是一个ViewModel
class GalleryViewModel @Inject constructor( // inject,告诉编译器,这个class构造器里面的内容被我注入了,麻烦帮我初始化它
    private val repository: UnsplashRepository // ViewModel是苦力,需要去仓库里面取一些需要的东西
) : ViewModel() {
    private var currentQueryValue: String? = null // 把搜索的参数保存起来
    private var currentSearchResult: Flow<PagingData<UnsplashPhoto>>? = null // 把搜索的结果保存起来

    fun searchPictures(queryString: String): Flow<PagingData<UnsplashPhoto>> {
        currentQueryValue = queryString
        val newResult: Flow<PagingData<UnsplashPhoto>> =
            repository.getSearchResultStream(queryString).cachedIn(viewModelScope)
        currentSearchResult = newResult
        return newResult
    }
}

在代码中,看到了构造器中注入了一个Repository,那这个Repository在我们的开发中又担任了什么角色呢?

Repository code

import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import com.google.samples.apps.sunflower.api.UnsplashService
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject

class UnsplashRepository @Inject constructor(private val service: UnsplashService) {

    fun getSearchResultStream(query: String): Flow<PagingData<UnsplashPhoto>> {
        return Pager(
            config = PagingConfig(enablePlaceholders = false, pageSize = NETWORK_PAGE_SIZE),
            pagingSourceFactory = { UnsplashPagingSource(service, query) }
        ).flow
    }

    companion object {
        private const val NETWORK_PAGE_SIZE = 25
    }
}

这时候我们看到repository里面又注入了一个service,这么复杂,又不知道是用来做什么的,为什么不能直接在ViewModel里面直接实现searchPictures的逻辑,何必又搞个Repository多此一举呢?不急,再看另一个类型的Repository

import javax.inject.Inject
import javax.inject.Singleton

/**
 * Repository module for handling data operations.
 *
 * Collecting from the Flows in [PlantDao] is main-safe.  Room supports Coroutines and moves the
 * query execution off of the main thread.
 */
@Singleton
class PlantRepository @Inject constructor(private val plantDao: PlantDao) {

    fun getPlants() = plantDao.getPlants()

    fun getPlant(plantId: String) = plantDao.getPlant(plantId)

    fun getPlantsWithGrowZoneNumber(growZoneNumber: Int) =
        plantDao.getPlantsWithGrowZoneNumber(growZoneNumber)
}

这个Repository结构也类似,看到注释里面给我们解释了一下:

Repository模块是用来处理数据操作的

所以这时候我们可以假设有这么一个场景:有个用户登录界面,用户登录成功后,需要查询到最近未读的消息,然后在主页面显示出来。

那么按照正常的MVVM的App Architecture来分析的话就是:
首先View(Activity / Fragment)填写用户名密码后通知ViewModel更改了Model,然后触发登录按钮,这时候应该发出第一个api请求了。我们说过,网络调用也同样是ViewModel先处理的,这时候ViewModel来到跟用户有关的Repository,这个Repository有登录、注册、重置密码等函数,ViewModelModel给了Repository,并点名需要调用登录有关的函数。Repository表示他只负责处理数据,并不发送请求,所以又把请求和Model继续交给了他的小弟,Service / DAOService在发出请求之后,把结果反馈给了他的直属上级RepositoryRepository转交给了ViewModelViewModel把结果填充到了合适的Model,然后通知View进行了场景的跳转(navigate)。

简单的交互流程

跟着流程图应该会更容易理解一些,差不多就是这样了。

上一篇 下一篇

猜你喜欢

热点阅读