Android MVVM ——Using Kotlin and RX实现ViewModel与Model

随着对公司现有MVP架构模式代码的逐渐不满,每每新增一个小功能,总要在一个又一个的接口中新增方法,代码变得越来越不清晰,迭代变得越来越困难。作为一个爱搞事的程序猿,免不了要考虑一波新的架构模式,MVVM也就映入眼帘。首先对比一下我们曾经的MVP和即将要使用的MVVM吧(MVC就离开历史舞台吧)。

为什么要淘汰MVP?

  • 太多的接口,一个功能处处接口
  • Presenter层与View层耦合严重
  • View层的一个改变往往牵动Presenter层,增加一个新方法往往要修改好几处地方
  • 每个Presenter无法重用,通常耦合到特定的View层

MVVM有什么优势?

  • 不再有过多的接口
  • ViewModel和DataModel支持单元测试
  • ViewModel不再与特定的View耦合
  • ViewModel可以在多个View中组合或使用

MVVM到底长什么样?

Android MVVM ——Using Kotlin and RX实现ViewModel与Model_第1张图片
MVVM模型.png

View:

Activity/Fragment/View

从ViewModel层获取UI数据

请求ViewModel对数据进行操作

ViewModel:

作为View和Model之间的桥梁

请求Model层的数据并为View层转换

使View层更新数据

Model:

作为DataModel/Repository

持久化业务逻辑

从多种数据源获取数据(DataBase,REST Api,cache)


实际使用

这一篇中,我不打算使用LiveData,LiveData在接下来的学习中我们会使用,而且将会相当的给力。先试试RX来实现数据Observe吧。

基本实现

下面这个例子,将会简单的从API请求用户信息列表并显示。

/** ViewModel */
class UserListViewModel(val userRepository: UserRepository){
    
    fun getUsers(): Observable {
        //获取用户数据
        return userRepository.getUser()
            .map { UserList(it, "Uses loaded success.") }
    }
    
}

/** Model */
class UserRepository(val userApi: UserApi){
    
    fun getUsers(): Observable> = 
        userApi.getUsers()
    
}

interface UserApi{
    
    @GET("users")
    fun getUsers(): Observabel>
    
}

UserListViewModle: ViewModel层,从Repository中获取数据,提供给View

UserRepository: 用户数据仓库,从数据库、网路Api获取数据

UserApi: 网路接口

改进Model层

我们可以修改UserRepository使它可以存储从API请求到的用户列表,这样每次我们调用getUser()方法中,我们会立即先返回缓存的用户列表,然后再返回API中的新数据。这可以使用RX中的mergeWith()方法来实现

class UserRepository(val userApi: UserApi){
    
    var cacheUsers = emptyList()
    
    fun getUsers(): Observale> = 
        if (cachedUsers.isEmpty())
            userApi.getUsers().doOnNext{ cachedUsers = it }
        else 
            Observable.just(cachedUsers)
                .mergeWith(userApi.getUsers())
                .doOnNext{ cachedUsers = it }

}

(PS: 通常在项目中,我们都会使用Dagger来提供UserRepository、UserApi的单例)

好处

这种方法中(将ViewModel与DataModel / Repository分开),ViewModel层不知道,也不关心数据的来源。它也不负责缓存或存储数据。我们能够在不对ViewModel进行任何更改的情况下引入API数据的缓存。

对UserRepository单元测试

我们可以使用TestObserver对UserRepository进行单元测试

class UserRepositoryTest{
    
    lateinit var userRepository: UserRepository
    lateinit var userApi: UserApi
    
    @Before
    fun setUp(){
        userApi = mock()
        userRepository = UserRepository(userApi)
    }
    
    @Test
    fun test_emptyCache_noDataOnApi_returnEmptyList(){
        `when`(userApi.getUsers()).thenReturn(Observable.just(emptyList()))
        
        userRepository.getUsers().test()
            .assertValue{ it.isEmpty() }

    }
    
    @Test
    fun test_emptyCache_hasDataOnApi_returnsApiData() {
        `when`(userApi.getUsers()).thenReturn(Observable.just(listOf(aRandomUser())))

        userRepository.getUsers().test()
                .assertValueCount(1)
                .assertValue { it.size == 1 }
    }

    @Test
    fun test_hasCacheData_hasApiData_returnsBothData() {
        val cachedData = listOf(aRandomUser())
        val apiData = listOf(aRandomUser(), aRandomUser())
        `when`(userApi.getUsers()).thenReturn(Observable.just(apiData))
        userRepository.cachedUsers = cachedData

        userRepository.getUsers().test()
                //Both cached & API data delivered
                .assertValueCount(2)
                //First cache data delivered
                .assertValueAt(0, { it == cachedData })
                //Secondly api data delivered
                .assertValueAt(1, { it == apiData })
    }

    @Test
    fun test_cache_updatedWithApiData() {
        val apiData = listOf(aRandomUser(), aRandomUser())
        `when`(userApi.getUsers()).thenReturn(Observable.just(apiData))

        userRepository.getUsers().test()

        assertEquals(userRepository.cachedUsers, apiData)
    }

    fun aRandomUser() = User("[email protected]", "John", UUID.randomUUID().toString().take(5))
    }
}

总结

这只是我个人对Android中MVVM当前状态和新架构组件的看法。选择适合自己需求的架构,适合自己的团队结构,保持代码清洁,帮助您轻松测试代码,最重要的是允许您轻松添加新功能。

你可能感兴趣的:(Android MVVM ——Using Kotlin and RX实现ViewModel与Model)