Verified
Status Update
Comments
yb...@google.com <yb...@google.com>
ta...@gmail.com <ta...@gmail.com> #2
Yigit, do you have time to fix it?
reemission of the same liveData is racy
reemission of the same liveData is racy
al...@gmail.com <al...@gmail.com> #4
Thanks for the detailed analysis. This may not be an issue anymore since we've started using Main.immediate there but I' not sure; I'll try to create a test case.
cc...@google.com <cc...@google.com> #5
just emitting same live data reproduces the issue.
@Test
fun raceTest() {
val subLiveData = MutableLiveData(1)
val subject = liveData(testScope.coroutineContext) {
emitSource(subLiveData)
emitSource(subLiveData) //crashes
}
subject.addObserver().apply {
testScope.advanceUntilIdle()
}
}
@Test
fun raceTest() {
val subLiveData = MutableLiveData(1)
val subject = liveData(testScope.coroutineContext) {
emitSource(subLiveData)
emitSource(subLiveData) //crashes
}
subject.addObserver().apply {
testScope.advanceUntilIdle()
}
}
al...@gmail.com <al...@gmail.com> #6
With 2.2.0-alpha04 (that use Main.immediate), the issue seems to be still there (I tested it by calling emitSource() twice, like your test case)
cc...@google.com <cc...@google.com> #7
yea sorry immediate does not fix it.
I actually have a WIP fix for it:
https://android-review.googlesource.com/c/platform/frameworks/support/+/1112186
if your case is the one i found (emitting same LiveData multiple times, as shown in #5) you can work around it by adding a dummy transformation.
val subLiveData = MutableLiveData(1)
val subject = liveData(testScope.coroutineContext) {
emitSource(subLiveData.map {it })
emitSource(subLiveData.map {it} )
}
I actually have a WIP fix for it:
if your case is the one i found (emitting same LiveData multiple times, as shown in #5) you can work around it by adding a dummy transformation.
val subLiveData = MutableLiveData(1)
val subject = liveData(testScope.coroutineContext) {
emitSource(subLiveData.map {it })
emitSource(subLiveData.map {it} )
}
[Deleted User] <[Deleted User]> #8
Project: platform/frameworks/support
Branch: androidx-master-dev
commit af12e75e6b4110f48e44ca121466943909de8f06
Author: Yigit Boyar <yboyar@google.com>
Date: Tue Sep 03 12:58:11 2019
Fix coroutine livedata race condition
This CL fixes a bug in liveData builder where emitting same
LiveData source twice would make it crash because the second
emission registry could possibly happen before first one is
removed as source.
We fix it by using a suspending dispose function. It does feel
a bit hacky but we cannot make DisposableHandle.dispose async
and we do not want to block there. This does not mean that there
is a problem if developer disposes it manually since our emit
functions take care of making sure it disposes (and there is
no other way to add source to the underlying MediatorLiveData)
Bug: 140249349
Test: BuildLiveDataTest#raceTest_*
Change-Id: I0b464c242a583da4669af195cf2504e2adc4de40
M lifecycle/lifecycle-livedata-ktx/api/2.2.0-alpha05.txt
M lifecycle/lifecycle-livedata-ktx/api/current.txt
M lifecycle/lifecycle-livedata-ktx/api/public_plus_experimental_2.2.0-alpha05.txt
M lifecycle/lifecycle-livedata-ktx/api/public_plus_experimental_current.txt
M lifecycle/lifecycle-livedata-ktx/api/restricted_2.2.0-alpha05.txt
M lifecycle/lifecycle-livedata-ktx/api/restricted_current.txt
M lifecycle/lifecycle-livedata-ktx/src/main/java/androidx/lifecycle/CoroutineLiveData.kt
M lifecycle/lifecycle-livedata-ktx/src/test/java/androidx/lifecycle/BuildLiveDataTest.kt
https://android-review.googlesource.com/1112186
https://goto.google.com/android-sha1/af12e75e6b4110f48e44ca121466943909de8f06
Branch: androidx-master-dev
commit af12e75e6b4110f48e44ca121466943909de8f06
Author: Yigit Boyar <yboyar@google.com>
Date: Tue Sep 03 12:58:11 2019
Fix coroutine livedata race condition
This CL fixes a bug in liveData builder where emitting same
LiveData source twice would make it crash because the second
emission registry could possibly happen before first one is
removed as source.
We fix it by using a suspending dispose function. It does feel
a bit hacky but we cannot make DisposableHandle.dispose async
and we do not want to block there. This does not mean that there
is a problem if developer disposes it manually since our emit
functions take care of making sure it disposes (and there is
no other way to add source to the underlying MediatorLiveData)
Bug: 140249349
Test: BuildLiveDataTest#raceTest_*
Change-Id: I0b464c242a583da4669af195cf2504e2adc4de40
M lifecycle/lifecycle-livedata-ktx/api/2.2.0-alpha05.txt
M lifecycle/lifecycle-livedata-ktx/api/current.txt
M lifecycle/lifecycle-livedata-ktx/api/public_plus_experimental_2.2.0-alpha05.txt
M lifecycle/lifecycle-livedata-ktx/api/public_plus_experimental_current.txt
M lifecycle/lifecycle-livedata-ktx/api/restricted_2.2.0-alpha05.txt
M lifecycle/lifecycle-livedata-ktx/api/restricted_current.txt
M lifecycle/lifecycle-livedata-ktx/src/main/java/androidx/lifecycle/CoroutineLiveData.kt
M lifecycle/lifecycle-livedata-ktx/src/test/java/androidx/lifecycle/BuildLiveDataTest.kt
mw...@gmail.com <mw...@gmail.com> #9
I have a problem that I believe is related to this.
Scenario 1:
When I load empty item, onZeroItemLoaded is called once. At that momen user have no data and all is well. But after t minutes the data for users are populated. At this point I try to scroll the Recyclerview and onZeroItemLoaded is never called again. AFAIK, it was supposed to be called. If am wrong can someone correct me?
Scenario 2: User logs into application and there are few items in the server (less than page size set in live page builder). onZeroItemLoaded is called and inserts data in room database, happy time! Then trying to scroll recylerview onItemEndLoaded is not being called. If I close app and start it again, this time onItemEndLoaded get called twice and it never get called again.
I have read all I can on the internet for days and could not find something that either shows of what am doing wrong or missing. So I concluded this might be a bug
Scenario 1:
When I load empty item, onZeroItemLoaded is called once. At that momen user have no data and all is well. But after t minutes the data for users are populated. At this point I try to scroll the Recyclerview and onZeroItemLoaded is never called again. AFAIK, it was supposed to be called. If am wrong can someone correct me?
Scenario 2: User logs into application and there are few items in the server (less than page size set in live page builder). onZeroItemLoaded is called and inserts data in room database, happy time! Then trying to scroll recylerview onItemEndLoaded is not being called. If I close app and start it again, this time onItemEndLoaded get called twice and it never get called again.
I have read all I can on the internet for days and could not find something that either shows of what am doing wrong or missing. So I concluded this might be a bug
yb...@google.com <yb...@google.com> #10
for S1: There is on indication that we can know if data is ready on the server so we cannot call onZeroItems loaded. Attempt the scroll RV won't trigger aynthing since it won't scroll.
for S2: same as above. When the list is loaded the first time, it triggers end load events since we've reched end of the list and laid it out. but after that, no scroll will happen and we don't know your server has more data.
Both of these problems boils down to the fact that your source (server) has more data but you don't know about it. You need to implement it yourself, and if you ever update the database, paging will automatically refresh.
for S2: same as above. When the list is loaded the first time, it triggers end load events since we've reched end of the list and laid it out. but after that, no scroll will happen and we don't know your server has more data.
Both of these problems boils down to the fact that your source (server) has more data but you don't know about it. You need to implement it yourself, and if you ever update the database, paging will automatically refresh.
mw...@gmail.com <mw...@gmail.com> #11
I think I misunderstood how the paging library works in such scenario. Can you help me understand when does paging kick in in let say scenario2? Is it only when items are more than page size defined in config?
I agree that I should write logic to check on the server. I had false assumption that trying to scroll triggers everything. I was wrong! I would appreciate your help on this question. I just don't want to reinvent the whole thing that paging already does!
I agree that I should write logic to check on the server. I had false assumption that trying to scroll triggers everything. I was wrong! I would appreciate your help on this question. I just don't want to reinvent the whole thing that paging already does!
yb...@google.com <yb...@google.com> #12
for sceneario 2, you really need a custom solution depending on your backend. There is no way paging can know the data on your server changed besides the invalidate() in the DataSource.
usually, these things are implemented as pull to refresh where user triggers a refresh and then you go and fetch new data.
Alternatively, your backend could be sending a push notification when data changes. It is usually unnecessary / overkill but depends on your use case.
Some apps handle this by having a persistent connection to their server to get these notifications and only keep that connection when app is in the foreground. Again, you need to implement it since it heavily relies on your backend.
usually, these things are implemented as pull to refresh where user triggers a refresh and then you go and fetch new data.
Alternatively, your backend could be sending a push notification when data changes. It is usually unnecessary / overkill but depends on your use case.
Some apps handle this by having a persistent connection to their server to get these notifications and only keep that connection when app is in the foreground. Again, you need to implement it since it heavily relies on your backend.
mw...@gmail.com <mw...@gmail.com> #13
Thank you for the comment. I went with refresh path and it's OK.
Description