性能比较:映射 LiveData 值与 Flow 值
Performance comparison: Mapping a LiveData value vs Flow value
目前,我正在 android 中对实时数据和流量进行一些试验。我感到困惑的是,在映射 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) }
现在是诡计部分:假设我们有一个要映射的实时数据值。由于映射是在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 的流,因此我们将其转换回实时数据值:
@JvmOverloads
public fun <T> Flow<T>.asLiveData(
context: CoroutineContext = EmptyCoroutineContext,
timeoutInMs: Long = DEFAULT_TIMEOUT
): LiveData<T> = liveData(context, timeoutInMs) {
collect {
emit(it)
}
}
现在问题:
- 与映射流值相比,映射实时数据值对性能影响大吗
- 将实时数据值转换为流值然后再将其转换回实时数据是否会引入任何开销和/或性能问题?如果是,直接映射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")
}
}
如果你想在不同的线程上执行映射,那么你应该使用 LiveData.switchMap
和 liveData(context) { }
livedata.switchMap {
liveData(Dispatchers.IO) {
emit(someValue)
}
}
或者摆脱 LiveData 并在任何地方使用 Flow/StateFlow/SharedFlow/Channel
目前,我正在 android 中对实时数据和流量进行一些试验。我感到困惑的是,在映射 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) }
现在是诡计部分:假设我们有一个要映射的实时数据值。由于映射是在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 的流,因此我们将其转换回实时数据值:
@JvmOverloads
public fun <T> Flow<T>.asLiveData(
context: CoroutineContext = EmptyCoroutineContext,
timeoutInMs: Long = DEFAULT_TIMEOUT
): LiveData<T> = liveData(context, timeoutInMs) {
collect {
emit(it)
}
}
现在问题:
- 与映射流值相比,映射实时数据值对性能影响大吗
- 将实时数据值转换为流值然后再将其转换回实时数据是否会引入任何开销和/或性能问题?如果是,直接映射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")
}
}
如果你想在不同的线程上执行映射,那么你应该使用 LiveData.switchMap
和 liveData(context) { }
livedata.switchMap {
liveData(Dispatchers.IO) {
emit(someValue)
}
}
或者摆脱 LiveData 并在任何地方使用 Flow/StateFlow/SharedFlow/Channel