LiveData

MutableLiveData

这是最常用的,最基本的一个LiveData


MediatorLiveData

继承自MutableLiveData。它可以观察其他的LiveData对象,并对来自它们的OnChanged事件做出反应。用来合并多个LiveData的值,并且统一做出回应。

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var   liveData1:LiveData = ...;
var liveData2:LiveData = ...;
var liveDataMerger = MediatorLiveData<T>()
liveDataMerger.addSource(liveData1, value -> liveDataMerger.setValue(value));
liveDataMerger.addSource(liveData2, value -> liveDataMerger.setValue(value));
liveDataMerger.addSource(liveData1, new Observer () {
private int count = 1;

@Override public void onChanged(@Nullable Integer s) {
count++;
liveDataMerger.setValue(s);
if (count > 10) {
liveDataMerger.removeSource(liveData1);
}
}
});

CoroutineLiveData

androidx.lifecycle:lifecycle-livedata-ktx:2.3.1

这是一个内部类,我们没法直接使用。使用方法直接通过 liveData() 的方式进行创建。注意:在liveData()创建一个livedata的时候,他的内部block会在livedata为活跃状态下自动触发。然后需要通过emit或者emitSource方式把结果发送出去。

通过名字就可以知道这是一个具有协程属性挂起的livedata,通过它可以发送延迟的数据,并且他也是和生命周期绑定的。因为使用它只能通过 liveData()这个方法创建,我们就来分析这个:

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

构建一个LiveData,其中包含在LiveDataScope上执行的给定块产生的值。当返回的LiveData变为活动时,块开始执行。如果在块执行时,LiveData变为非活动状态,它将在timeoutInMs毫秒之后被取消,除非LiveData在该超时之前再次变为活动状态(以优雅地处理活动旋转等情况)。LiveDataScope任何值。从已取消的块发出的将被忽略。取消之后,如果LiveData再次激活,则将从头重新执行该块。如果你愿意的话。

那这个timeoutInMs是怎么起作用的呢。这里看源码:

在CoroutineLiveData类里面的onInactive(非活跃)方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
internal class CoroutineLiveData<T>(
context: CoroutineContext = EmptyCoroutineContext,
timeoutInMs: Long = DEFAULT_TIMEOUT,
block: Block<T>
) : MediatorLiveData<T>() {
private var blockRunner: BlockRunner<T>?

override fun onActive() {
super.onActive()
blockRunner?.maybeRun()
}

// 当livedata处于非活跃状态 会被系统触发
override fun onInactive() {
super.onInactive()
blockRunner?.cancel()
}
}
internal class BlockRunner<T>(
private val liveData: CoroutineLiveData<T>,
private val block: Block<T>,
private val timeoutInMs: Long,
private val scope: CoroutineScope,
private val onDone: () -> Unit
) {
@MainThread
fun cancel() {
if (cancellationJob != null) {
error("Cancel call cannot happen without a maybeRun")
}
cancellationJob = scope.launch(Dispatchers.Main.immediate) {
// 可以看到这里开启了一个协程,他会延迟我们设定的timeoutInMs时间后在执行,如果5秒内livedata还出去非活跃状态,那么这次事件就执行cancel方法。
delay(timeoutInMs)
if (!liveData.hasActiveObservers()) {
runningJob?.cancel()
runningJob = null
}
}
}
}

案例:

1
2
3
4
val data : LiveData<Int> = liveData {
delay(3000)
emit(3)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 一个基于' userId '获取' User '对象并每30秒刷新一次的LiveData
// as long as it is observed
val userId : LiveData<String> = ...
val user = userId.switchMap { id ->
liveData {
while(true) {
// note that `while(true)` is fine because the `delay(30_000)` below will cooperate in
// cancellation if LiveData is not actively observed anymore
val data = api.fetch(id) // errors are ignored for brevity
emit(data)
delay(30_000)
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// A retrying data fetcher with doubling back-off
val user = liveData {
var backOffTime = 1_000
var succeeded = false
while(!succeeded) {
try {
emit(api.fetch(id))
succeeded = true
} catch(ioError : IOException) {
delay(backOffTime)
backOffTime *= minOf(backOffTime * 2, 60_000)
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// a LiveData that tries to load the `User` from local cache first and then tries to fetch
// from the server and also yields the updated value
val user = liveData {
// dispatch loading first
emit(LOADING(id))
// check local storage
val cached = cache.loadUser(id)
if (cached != null) {
emit(cached)
}
if (cached == null || cached.isStale()) {
val fresh = api.fetch(id) // errors are ignored for brevity
cache.save(fresh)
emit(fresh)
}
}
1
2
3
4
5
6
7
8
9
10
11
// a LiveData that immediately receives a LiveData<User> from the database and yields it as a
// source but also tries to back-fill the database from the server
val user = liveData {
val fromDb: LiveData<User> = roomDatabase.loadUser(id)
emitSource(fromDb)
val updated = api.fetch(id) // errors are ignored for brevity
// Since we are using Room here, updating the database will update the `fromDb` LiveData
// that was obtained above. See Room's documentation for more details.
// https://developer.android.com/training/data-storage/room/accessing-data#query-observable
roomDatabase.insert(updated)
}