Status Update
Comments
sk...@gmail.com <sk...@gmail.com> #2
Mistyped title, sorry. ~> "on already cancelled block".
yb...@google.com <yb...@google.com>
b9...@gmail.com <b9...@gmail.com> #3
I meet the same issue too, especially in this scenario when liveData already has data:
val liveData = flow.asLiveData()
mediatorLiveData.addSource(liveData) {
removeSource(liveData)
}
This way CoroutineLiveData will trigger onInactive
immediately within super.onActive()
block, so that blockRunner?.cancel()
will be called right before blockRunner?.maybeRun()
.
yb...@google.com <yb...@google.com> #4
Thanks! That was the key information. I can reproduce this now:
@Test
fun startStopImmediately(): Unit {
runBlocking {
val mediator = withContext(Dispatchers.Main) {
val mediator = MediatorLiveData<Int>()
val liveData = channelFlow {
send(1)
delay(30000) // prevent block from ending
}.asLiveData()
mediator.addSource(liveData) {
mediator.removeSource(liveData)
mediator.value = -it
}
mediator
}
val read = mediator.asFlow().first()
Truth.assertThat(read).isEqualTo(-1)
}
}
yb...@google.com <yb...@google.com> #5
ugh we are getting two inactive calls :/, there should only be 1.
2020-06-03 14:57:59.082 10426-10426 D/COROUTINE_LIVE_DATA: onActive androidx.lifecycle.CoroutineLiveData@3ed3643
java.lang.RuntimeException
at androidx.lifecycle.CoroutineLiveData.onActive(CoroutineLiveData.kt:241)
at androidx.lifecycle.LiveData$ObserverWrapper.activeStateChanged(LiveData.java:437)
at androidx.lifecycle.LiveData.observeForever(LiveData.java:232)
at androidx.lifecycle.MediatorLiveData$Source.plug(MediatorLiveData.java:141)
at androidx.lifecycle.MediatorLiveData.onActive(MediatorLiveData.java:118)
at androidx.lifecycle.LiveData$ObserverWrapper.activeStateChanged(LiveData.java:437)
at androidx.lifecycle.LiveData.observeForever(LiveData.java:232)
at androidx.lifecycle.FlowLiveDataConversions$asFlow$1$1.invokeSuspend(FlowLiveData.kt:92)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:56)
at android.os.Handler.handleCallback(Handler.java:883)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7356)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
2020-06-03 14:57:59.097 10426-10426 D/COROUTINE_LIVE_DATA: onInactive androidx.lifecycle.CoroutineLiveData@3ed3643
java.lang.RuntimeException
at androidx.lifecycle.CoroutineLiveData.onInactive(CoroutineLiveData.kt:247)
at androidx.lifecycle.LiveData$ObserverWrapper.activeStateChanged(LiveData.java:440)
at androidx.lifecycle.LiveData.removeObserver(LiveData.java:248)
at androidx.lifecycle.MediatorLiveData$Source.unplug(MediatorLiveData.java:145)
at androidx.lifecycle.MediatorLiveData.removeSource(MediatorLiveData.java:110)
at androidx.lifecycle.FlowAsLiveDataIntegrationTest$startStopImmediately$1$mediator$1$1.onChanged(FlowAsLiveDataIntegrationTest.kt:44)
at androidx.lifecycle.FlowAsLiveDataIntegrationTest$startStopImmediately$1$mediator$1$1.onChanged(FlowAsLiveDataIntegrationTest.kt:33)
at androidx.lifecycle.MediatorLiveData$Source.onChanged(MediatorLiveData.java:152)
at androidx.lifecycle.LiveData.considerNotify(LiveData.java:131)
at androidx.lifecycle.LiveData.dispatchingValue(LiveData.java:149)
at androidx.lifecycle.LiveData.setValue(LiveData.java:307)
at androidx.lifecycle.MutableLiveData.setValue(MutableLiveData.java:50)
at androidx.lifecycle.LiveDataScopeImpl$emit$2.invokeSuspend(CoroutineLiveData.kt:101)
at androidx.lifecycle.LiveDataScopeImpl$emit$2.invoke(Unknown Source:10)
at kotlinx.coroutines.intrinsics.UndispatchedKt.startUndispatchedOrReturn(Undispatched.kt:91)
at kotlinx.coroutines.BuildersKt__Builders_commonKt.withContext(Builders.common.kt:154)
at kotlinx.coroutines.BuildersKt.withContext(Unknown Source:1)
at androidx.lifecycle.LiveDataScopeImpl.emit(CoroutineLiveData.kt:99)
at androidx.lifecycle.FlowLiveDataConversions$asLiveData$1$invokeSuspend$$inlined$collect$1.emit(Collect.kt:137)
at kotlinx.coroutines.flow.FlowKt__ChannelsKt.emitAllImpl$FlowKt__ChannelsKt(Channels.kt:59)
at kotlinx.coroutines.flow.FlowKt__ChannelsKt$emitAllImpl$1.invokeSuspend(Unknown Source:11)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:56)
at kotlinx.coroutines.EventLoop.processUnconfinedEvent(EventLoop.common.kt:69)
at kotlinx.coroutines.DispatchedContinuationKt.resumeCancellableWith(DispatchedContinuation.kt:321)
at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:26)
at kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt:109)
at kotlinx.coroutines.AbstractCoroutine.start(AbstractCoroutine.kt:158)
at kotlinx.coroutines.BuildersKt__Builders_commonKt.launch(Builders.common.kt:54)
at kotlinx.coroutines.BuildersKt.launch(Unknown Source:1)
at kotlinx.coroutines.BuildersKt__Builders_commonKt.launch$default(Builders.common.kt:47)
at kotlinx.coroutines.BuildersKt.launch$default(Unknown Source:1)
at androidx.lifecycle.BlockRunner.maybeRun(CoroutineLiveData.kt:176)
at androidx.lifecycle.CoroutineLiveData.onActive(CoroutineLiveData.kt:243)
at androidx.lifecycle.LiveData$ObserverWrapper.activeStateChanged(LiveData.java:437)
at androidx.lifecycle.LiveData.observeForever(LiveData.java:232)
at androidx.lifecycle.MediatorLiveData$Source.plug(MediatorLiveData.java:141)
at androidx.lifecycle.MediatorLiveData.onActive(MediatorLiveData.java:118)
at androidx.lifecycle.LiveData$ObserverWrapper.activeStateChanged(LiveData.java:437)
at androidx.lifecycle.LiveData.observeForever(LiveData.java:232)
at androidx.lifecycle.FlowLiveDataConversions$asFlow$1$1.invokeSuspend(FlowLiveData.kt:92)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:56)
at android.os.Handler.handleCallback(Handler.java:883)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7356)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
2020-06-03 14:57:59.098 10426-10426 D/COROUTINE_LIVE_DATA: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
2020-06-03 14:57:59.098 10426-10426 D/COROUTINE_LIVE_DATA: onInactive androidx.lifecycle.CoroutineLiveData@3ed3643
java.lang.RuntimeException
at androidx.lifecycle.CoroutineLiveData.onInactive(CoroutineLiveData.kt:247)
at androidx.lifecycle.LiveData$ObserverWrapper.activeStateChanged(LiveData.java:440)
at androidx.lifecycle.LiveData.observeForever(LiveData.java:232)
at androidx.lifecycle.MediatorLiveData$Source.plug(MediatorLiveData.java:141)
at androidx.lifecycle.MediatorLiveData.onActive(MediatorLiveData.java:118)
at androidx.lifecycle.LiveData$ObserverWrapper.activeStateChanged(LiveData.java:437)
at androidx.lifecycle.LiveData.observeForever(LiveData.java:232)
at androidx.lifecycle.FlowLiveDataConversions$asFlow$1$1.invokeSuspend(FlowLiveData.kt:92)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:56)
at android.os.Handler.handleCallback(Handler.java:883)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7356)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
yb...@google.com <yb...@google.com> #6
sk...@gmail.com <sk...@gmail.com> #7
Thanks for reproducer and so quick fix.
b9...@gmail.com <b9...@gmail.com> #8
Thanks! Will it land on next Lifecycle alpha release?
yb...@google.com <yb...@google.com> #9
it won't make it to the next version (the built was already cut for the postponed Android11 launch). Should go out the version after :) but meanwhile, you can try using the snapshot (androidx.dev). we don't really play w/ this codebase much so it should be fairly stable :)
ap...@google.com <ap...@google.com> #10
Branch: androidx-master-dev
commit 4a302c8fadff7fefa27c7172d77d1128a2fa8f13
Author: Yigit Boyar <yboyar@google.com>
Date: Wed Jun 03 16:53:27 2020
Handle re-entry in live data
This CL fixes a bug where LiveData would not properly handle
re-entry in its onActive method.
There are two cases:
* onActive change method was not handling re-entry, causing
it to possibly make duplicate calls to onActive/inactive.
-> Now we guard this behind a counter to handle re-entry
* dispatch when observer is added would not handle cases where
lifecycle might change state while we are dispatching the value.
-> Now we check for state again after dispatching and if it
changed, we trigger onActive again.
Bug: 157840298
Test: FlowAsLiveDataIntegrationTest, LiveDataTest
Change-Id: I4d368886288a435f65a4db7529d987d0401b25a7
M lifecycle/lifecycle-livedata-core/src/main/java/androidx/lifecycle/LiveData.java
M lifecycle/lifecycle-livedata-core/src/test/java/androidx/lifecycle/LiveDataTest.java
M lifecycle/lifecycle-livedata-ktx/build.gradle
A lifecycle/lifecycle-livedata-ktx/src/androidTest/java/androidx.lifecycle/FlowAsLiveDataIntegrationTest.kt
yb...@google.com <yb...@google.com>
an...@gmail.com <an...@gmail.com> #11
I'm getting this same java.lang.IllegalStateException: Cancel call cannot happen without a maybeRun
exception every so often (on devices running production, but not locally during debug).
Is there some other way I can work around this (besides taking a dependency on androidx.dev)?
I have two StateFlows (The values change based on logic in background Workers)
private val _activeCycleIdFlow = MutableStateFlow(0)
private val _latestCycleIdFlow = MutableStateFlow(0)
whose results are combined
val newCycleAvailable: Flow<Boolean>
get() = _activeCycleIdFlow.combine(_latestCycleIdFlow) { a, b -> a != b }
Which is used as follows:
val newCycle: LiveData<Boolean> = cycleRepo.newCycleAvailable.asLiveData()
yb...@google.com <yb...@google.com> #12
unfortunately i cannot think of an easy way to work around this besides copying the asLiveData
code and removing the exception (it won't hurt actually, it is just consistency check to find such bugs).
Depending on which version you are in, depending on androidx.dev may not be too bad as it fairly stable library. If I were you, i would just pull in my own asLiveData :(
an...@gmail.com <an...@gmail.com> #13
an...@gmail.com <an...@gmail.com> #14
java.lang.IllegalStateException
before emitting? There isn't much else to asLiveData
.
@InternalCoroutinesApi
val newCycle: LiveData<Boolean> = liveData {
cycleRepo.newCycleAvailable
.catch {
// ignore IllegalStateException and log cause
}
.collect {
emit(it)
}
}
yb...@google.com <yb...@google.com> #15
catching exceptions thrown from the pipeline is not a great idea as it might leave it in an inconsistent state (because the code that is intended to run after it won't run). It is better to just copy it w/o the check
an...@gmail.com <an...@gmail.com> #16
Okay, I'm clearly missing something; when I open the code (ctrl+click) for asLiveData
(from FlowLiveData.kt
), it seems very simple, and I don't see any checks. This is it:
@JvmOverloads
fun <T> Flow<T>.asLiveData(
context: CoroutineContext = EmptyCoroutineContext,
timeoutInMs: Long = DEFAULT_TIMEOUT
): LiveData<T> = liveData(context, timeoutInMs) {
collect {
emit(it)
}
}
If I do the following am I not just duplicating the above?
val newCycle: LiveData<Boolean> = liveData {
cycleRepo.newCycleAvailable
.collect {
emit(it)
}
}
an...@gmail.com <an...@gmail.com> #18
Thanks. That makes more sense to me :)
an...@gmail.com <an...@gmail.com> #19
So I changed my code back to using .asLiveData()
and modified my gradle scripts to include
allprojects {
repositories {
...
maven { url 'https://androidx.dev/snapshots/builds/6603247/artifacts/repository' }
}
}
and
def lifecycle_version = "2.3.0-SNAPSHOT"
I compared the CoroutineLiveData.kt
for the different versions (alpha4, snapshot) and they are identical. I take it the fix is elsewhere? Is it publicly available? Sorry I don't normally use snapshot builds of libraries.
yb...@google.com <yb...@google.com> #20
yea the fix is actually in core.
i was recommending just removing the check for now, it won't hurt as it is more of a state check to catch these bugs.
but i'm thinking, maybe you should just move to snapshot. also a new version is coming w/ the fix soon (i think next week)
an...@gmail.com <an...@gmail.com> #21
#20,
Thank you.
fi...@colendi.com <fi...@colendi.com> #22
Does anyone have a suggestion?
Description
Component used:
androidx.lifecycle:lifecycle-livedata-ktx
androidx.lifecycle:lifecycle-runtime
androidx.lifecycle:lifecycle-runtime-ktx
androidx.lifecycle:lifecycle-livedata-ktx
androidx.lifecycle:lifecycle-viewmodel-ktx
androidx.lifecycle:lifecycle-common-java8
Version used: 2.3.0-alpha02
Devices/Android versions reproduced on: OnePlus6T, Samsungs, Xiaomis, etc.
We are using
Flow.asLiveData()
converter. We receivejava.lang.IllegalStateException: Cancel call cannot happen without a maybeRun
exceptions on production. Unable to reproduce locally. Seems like some race condition is happening in LiveData.