0%

简易MVVM网络请求框架(一些解析)

ViewModel实现

1
2
3
4
5
6
7
8
9
10
11
class TestViewModel : ViewModel() {
private val testRepo = TestRepo(viewModelScope.coroutineContext)
private val _uidData = MutableLiveData<String>()
val userInfoData = _uidData.switchMap {
testRepo.getUserInfo(it)
}

fun setUserId(uid: String) {
_uidData.value = uid
}
}

代码量很少,涉及到哪些知识点呢?

  1. switchMap

    其实就是Transformations.switchMap(),简述就是_uidData.value有了更新(不管值有没有发生改变),则会通知到switchMap执行其后面的代码块testRepo.getUserInfo(it)

    1
    2
    3
    4
    5
    6
    //信息量更多的代码
    val userInfoData: LiveData<Resource<UserInfo>> = _uidData.switchMap {
    uid ->
    val result: LiveDdata<Resource<UserInfo>> = testRepo.getUserInfo(uid)
    return result
    }

    userInfoData其实是一个MediatorLiveData

  2. MediatorLiveData

    可以同时监听很多个LiveData源,查看MediatorLiveData源码知道在onActive/onInactive会添加监听/移除监听各个LiveData源~

  3. viewModelScope

    [viewModelScope] tied to this [ViewModel].
    This scope will be canceled when ViewModel will be cleared, i.e [ViewModel.onCleared] is called

尽管ViewModel里面没看到清理代码,实际上是有的,当activity.onDestroy后,ViewModel.clear会被调用,viewModelScope.cancel也会被调用,viewModelScope.cancel会导致正在请求的API被取消的。所以activity关闭后,正在请求的API也会被取消,这很好。当然如果你不想API被取消,那你就不要使用viewModelScope。

我们在Repo里面开启的协程,使用的是viewModelScope.coroutineContext,当viewModelScope被取消后,其子协程同样会被取消的。所以我们向TestRepo传递了viewModelScope.coroutineContext

private val testRepo = TestRepo(viewModelScope.coroutineContext)

Repo的实现

主要需要理解LiveDataScope,看下liveData的源码

1
2
3
4
5
fun <T> liveData(
context: CoroutineContext = EmptyCoroutineContext,
timeoutInMs: Long = DEFAULT_TIMEOUT,
@BuilderInference block: suspend LiveDataScope<T>.() -> Unit
): LiveData<T> = CoroutineLiveData(context, timeoutInMs, block)

第一个参数是协程的上下文,我们要与ViewModel同步,所以用的是viewModelScope.coroutineContext

第二个参数是超时时间,liveData会返回CoroutineLiveData,当CoroutineLiveData.hasActiveObservers == false时,即开始倒计时(当然如果block代码已执行完,那就不会开启倒计时了),超时后会取消第三个参数block代码块的执行(如果没执行完)。实际情况来说就是block执行的是网络请求,那么在Activity.onStop后,开始倒计时,倒计时到达后如果block没执行完,则会取消其执行,然后在Activity.onStart后,block代码块会重新执行。但是如果在倒计时前block代码块已执行完,将不会再重新执行。

要说明一下,这里的取消执行并不是因为viewModelScope.cancel被调用了,而是因为CoroutineLiveData的onInactivie被调用了,进而发生的取消

其他

我们再次回到ViewModel的实现

fun setUserId(uid: String)

如果连续多次调用这个函数会发生什么呢?调用一次就会做一次网络请求,如果在下次调用前网络返回了数据,那很正常的一次请求。但是如果网络没返回数据,而再次调用了该函数,那么前一次的网络请求会被取消,是立刻取消吗?这个要看liveDatatimeoutInMs超时参数值,如果你设置为0,那就是立刻取消的。默认是5000ms倒计时,即使倒计时到达前返回了数据,也不会再传递给userInfoData了。。这个看下switchMap的源码即可理解