【发布时间】:2021-08-11 10:19:34
【问题描述】:
目前,我正在 android 中尝试一些 livedata 和 flow。我不解的是,在映射 livedata 值和 flow 值时是否存在很大的性能差异,因为 livedata 映射是在主 ui 线程中执行的:
/**
* Returns a [LiveData] mapped from `this` LiveData by applying [transform] to each value set on
* `this` LiveData.
*
* This method is analogous to [io.reactivex.Observable.map].
*
* [transform] will be executed on the main thread.
*
* [...]
**/
public inline fun <X, Y> LiveData<X>.map(crossinline transform: (X) -> Y): LiveData<Y> =
Transformations.map(this) { transform(it) }
现在是诡计部分:假设我们有一个要映射的 livedata 值。由于映射是在 ui 线程中执行的,我们将 livedata 值转换为流值:
public fun <T> LiveData<T>.asFlow(): Flow<T> = flow {
val channel = Channel<T>(Channel.CONFLATED)
val observer = Observer<T> {
channel.offer(it)
}
withContext(Dispatchers.Main.immediate) {
observeForever(observer)
}
try {
for (value in channel) {
emit(value)
}
} finally {
GlobalScope.launch(Dispatchers.Main.immediate) {
removeObserver(observer)
}
}
}
将 livedata 观察者转换为流后,我们将值成功映射到另一个线程 (Dispatchers.IO)。但是现在我们不需要 ui 的流,所以我们将其转换回 livedata 值:
@JvmOverloads
public fun <T> Flow<T>.asLiveData(
context: CoroutineContext = EmptyCoroutineContext,
timeoutInMs: Long = DEFAULT_TIMEOUT
): LiveData<T> = liveData(context, timeoutInMs) {
collect {
emit(it)
}
}
现在问题:
- 与映射流值相比,映射 livedata 值是否对性能影响较大
- 将 livedata 值转换为流值,然后将其转换回 livedata 是否会引入任何开销和/或性能问题?如果是,直接映射 livedata 值会更好吗?
一个映射示例是:
fun LiveData<WorkInfo>.collectStatus(): LiveData<Status<Unit>> = map { workInfo ->
when(workInfo.state) {
WorkInfo.State.ENQUEUED -> Status.loading()
WorkInfo.State.RUNNING -> Status.loading()
WorkInfo.State.SUCCEEDED -> Status.success(Unit)
WorkInfo.State.BLOCKED -> Status.failed("Workmanager blocked")
WorkInfo.State.FAILED -> Status.failed("Workmanager failed")
WorkInfo.State.CANCELLED -> Status.failed("Workmanager cancelled")
}
}
【问题讨论】:
-
这取决于你所说的性能。除了线程切换的相同工作之外,还有微不足道的额外开销。但是,如果在工作完成时 UI 动画正在进行,则在主线程之外进行工作会使 UI 动画更流畅,前提是有足够的工作要做才能被察觉。我认为 LiveData 和 Flows 之间的性能没有任何显着差异,但使用纯 LiveData 时,映射到不同线程上的代码可能更加复杂。
-
@Tenfour04 但是
switchMap与liveData(context)组合是否切换线程? -
是的。
liveData启动一个在后台运行的协程以发布到 LiveData。 -
您的映射转换示例的成本约为几纳秒。将任务从一个线程移交给另一个线程的成本在微秒范围内。所以那是一千倍。相比之下,包装/展开是微不足道的。
标签: android kotlin kotlin-coroutines android-livedata