Status Update
Comments
an...@google.com <an...@google.com>
ap...@google.com <ap...@google.com> #2
@Override
public void onCreate() {
super.onCreate();
mWorkManagerImpl = WorkManagerImpl.getInstance(getApplicationContext());
if (mWorkManagerImpl == null) {
// This can occur if...
// 1. The app is performing an auto-backup. Prior to O, JobScheduler could erroneously
// try to send commands to JobService in this state (
// Application#onCreate nor ContentProviders have run, WorkManager won't be
// initialized. In this case, we should ignore all JobScheduler commands and tell it
// to retry.
But, current WorkManagerImpl.getInstance(Context context) implementation is that, it will never return null but throw IllegalStateException.
Isn't there's conflict among these 2 SystemJobService.onCreate logic, and WorkManagerImpl.getInstance logic?
b9...@gmail.com <b9...@gmail.com> #3
Branch: androidx-master-dev
commit e14104eb7008f82edd50b37e16bd119b307e0554
Author: Sumir Kataria <sumir@google.com>
Date: Mon Jun 24 10:50:57 2019
Fix for JobScheduler calls when backup is in progress.
The original fix (ag/Ia1604b7c4b98f6b31869e1161a0c1efce7fe46f2) relied
on a null-check, but the new version of WorkManager.getInstance(Context)
with the Context param doesn't return null; it throws an Exception. This
CL updates the three places where we explicitly check for the return value
internally:
1. SystemJobService (the main place where this would manifest)
2. RescheduleReceiver
3. WorkManagerTestInitHelper (for consistency)
Fixes: 135858602
Test: Ran existing tests
Change-Id: Ia232869712d0c78fd80e91d0a863b8c50a080400
M work/workmanager-testing/src/main/java/androidx/work/testing/WorkManagerTestInitHelper.java
M work/workmanager/src/main/java/androidx/work/impl/background/systemalarm/RescheduleReceiver.java
M work/workmanager/src/main/java/androidx/work/impl/background/systemjob/SystemJobService.java
an...@google.com <an...@google.com> #4
May I know when will it be ready for production? As, currently, this IllegalStateException is impacting our users.
b9...@gmail.com <b9...@gmail.com> #5
an...@google.com <an...@google.com> #6
Yes. We don't need on-demand initialization. May I know how does using old (deprecated) WorkManager.getInstance() in our app code, able to bypass this problem? Isn't the crash is origin from library internal SystemJobService?
We once thinking to bypass this problem, by switching back to Version 2.0.1 (The version just before WorkManager.getInstance(Context) is introduced). However, we notice Room version is being changed in between. Downgrading WorkManager version, might cause additional SQLite problem.
b9...@gmail.com <b9...@gmail.com> #7
I am curious: do you actually have reports of this bug on Android 9? It should've been fixed in the platform layer at that point. If possible, can you share any Android 9 specific logs/bugreports/stacktraces with device models?
an...@google.com <an...@google.com> #8
Based on your code comment, I thought this problem shouldn't occur in new Android but it happens some how. This is the log
Asus ZenFone 5 (ZE620KL) (ASUS_X00QD), Android 9 (Happen 2 times)
Asus ZenFone 5Z (ZS620KL/ZS621KL) (ASUS_Z01R_1), Android 9 (Happen 1 time)
3 incidents are having same log trace as below
java.lang.RuntimeException:
at android.app.ActivityThread.handleCreateService (ActivityThread.java:3570)
at android.app.ActivityThread.access$1300 (ActivityThread.java:200)
at android.app.ActivityThread$H.handleMessage (ActivityThread.java:1672)
at android.os.Handler.dispatchMessage (Handler.java:106)
at android.os.Looper.loop (Looper.java:198)
at android.app.ActivityThread.main (ActivityThread.java:6732)
at java.lang.reflect.Method.invoke (Method.java)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:858)
Caused by: java.lang.IllegalStateException:
at androidx.work.impl.WorkManagerImpl.getInstance (WorkManagerImpl.java:142)
at androidx.work.impl.background.systemjob.SystemJobService.onCreate (SystemJobService.java:53)
at android.app.ActivityThread.handleCreateService (ActivityThread.java:3558)
at android.app.ActivityThread.access$1300 (ActivityThread.java:200)
at android.app.ActivityThread$H.handleMessage (ActivityThread.java:1672)
at android.os.Handler.dispatchMessage (Handler.java:106)
at android.os.Looper.loop (Looper.java:198)
at android.app.ActivityThread.main (ActivityThread.java:6732)
at java.lang.reflect.Method.invoke (Method.java)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:858)
I attach the screenshot of Google Play Console as well. (I expect the stack trace should print programmer's message side-by-side with IllegalStateException. But it doesn't)
Feel free to let me know if you need any information from my side. Happy to help.
da...@gmail.com <da...@gmail.com> #9
vi...@gmail.com <vi...@gmail.com> #10
Thanks.
[Deleted User] <[Deleted User]> #11
an...@google.com <an...@google.com> #12
trace:
5 java.lang.IllegalStateException:WorkManager needs to be initialized via a ContentProvider#onCreate() or an Application#onCreate().
6 androidx.work.impl.background.systemjob.SystemJobService.onCreate(SystemJobService.java:68)
7 android.app.ActivityThread.handleCreateService(ActivityThread.java:3133)
8 android.app.ActivityThread.access$1900(ActivityThread.java:196)
9 android.app.ActivityThread$H.handleMessage(ActivityThread.java:1622)
10 com.android.tony.defenselib.hook.HookHandle$1.handleMessage(HookHandle.java:30)
11 android.os.Handler.dispatchMessage(Handler.java:107)
12 android.os.Looper.loop(Looper.java:210)
13 android.app.ActivityThread.main(ActivityThread.java:5982)
14 java.lang.reflect.Method.invoke(Native Method)
15 com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:852)
16 com.android.internal.os.ZygoteInit.main(ZygoteInit.java:742)
this code cause crash:
if (!Application.class.equals(getApplication().getClass())) {
// During auto-backup, we don't get a custom Application subclass. This code path
// indicates we are either performing auto-backup or the user never used a custom
// Application class (or both).
throw new IllegalStateException("WorkManager needs to be initialized via a "
+ "ContentProvider#onCreate() or an Application#onCreate().");
}
my analysis:
code in handleCreateService() in ActivityThread.java:
" Application app = packageInfo.makeApplication(false, mInstrumentation);
service.attach(context, this,
"
handleCreateService--->. Application app = packageInfo.makeApplication(false, mInstrumentation); ---> forceDefaultAppClass is always false-->if have custom Application,will make custom Application ---> service.attach(context, this,
--> if have customApplication, the code : "Application.class.equals(getApplication().getClass()" in Service will be false , even it's in backup mode
[Deleted User] <[Deleted User]> #13
compileSdkVersion 30
implementation 'androidx.work:work-runtime-ktx:2.4.0'
Crash
TCT (Alcatel) 5056D
Android 6.0 (SDK 23)
48.aab
2 hours ago
java.lang.RuntimeException:
at android.app.ActivityThread.handleCreateService (ActivityThread.java:2887)
at android.app.ActivityThread.access$1900 (ActivityThread.java:150)
at android.app.ActivityThread$H.handleMessage (ActivityThread.java:1427)
at android.os.Handler.dispatchMessage (Handler.java:102)
at android.os.Looper.loop (Looper.java:148)
at android.app.ActivityThread.main (ActivityThread.java:5417)
at java.lang.reflect.Method.invoke (Method.java)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run (ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:616)
Caused by: java.lang.IllegalStateException:
at androidx.work.impl.background.systemjob.SystemJobService.onCreate (SystemJobService.java:2)
at android.app.ActivityThread.handleCreateService (ActivityThread.java:2877)
at android.app.ActivityThread.access$1900 (ActivityThread.java:150)
at android.app.ActivityThread$H.handleMessage (ActivityThread.java:1427)
at android.os.Handler.dispatchMessage (Handler.java:102)
at android.os.Looper.loop (Looper.java:148)
at android.app.ActivityThread.main (ActivityThread.java:5417)
at java.lang.reflect.Method.invoke (Method.java)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run (ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:616)
What could the walkaround this issue? Is there another way to initialize the WorkManager instance? I'm using
private fun getWorkManager(): WorkManager =
try {
WorkManager.getInstance(App.context)
} catch (e: Exception) {
e.printStackTrace()
sleep(100)
getWorkManager()
}
eu...@gmail.com <eu...@gmail.com> #14
Library version: androidx.work:work-runtime:2.4.0
Samsung Galaxy S8 (dreamlte), 3840MB RAM, Android 9
java.lang.RuntimeException:
at android.app.ActivityThread.handleBindApplication (ActivityThread.java:6227)
at android.app.ActivityThread.access$1200 (ActivityThread.java:237)
at android.app.ActivityThread$H.handleMessage (ActivityThread.java:1785)
at android.os.Handler.dispatchMessage (Handler.java:106)
at android.os.Looper.loop (Looper.java:214)
at android.app.ActivityThread.main (ActivityThread.java:7050)
at java.lang.reflect.Method.invoke (Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:494)
at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:965)
Caused by: java.lang.IllegalStateException:
at androidx.work.impl.WorkManagerImpl.getInstance (WorkManagerImpl.java:150)
at androidx.work.WorkManager.getInstance (WorkManager.java:184)
at br.com.rcgm.dicaslotofacilfull.task.WorkRequestSincronizarConcurso.enfileirar (WorkRequestSincronizarConcurso.java:20)
at br.com.rcgm.dicaslotofacilfull.BaseApplication.onCreate (BaseApplication.java:23)
at android.app.Instrumentation.callApplicationOnCreate (Instrumentation.java:1154)
at android.app.ActivityThread.handleBindApplication (ActivityThread.java:6222)
da...@gmail.com <da...@gmail.com> #15
I'm also facing same issue, in few devices.
Instance access : WorkManager.getInstance()
I'm not doing on-demand Initialization.
version used: 'androidx.work:work-runtime:2.3.4'
Device details
Brand: samsung
Model: Galaxy J2 Pro
Android version: 6.0.1
Fatal Exception: java.lang.RuntimeException: Unable to create service androidx.work.impl.background.systemjob.SystemJobService: java.lang.IllegalStateException: WorkManager needs to be initialized via a ContentProvider#onCreate() or an Application#onCreate().
at android.app.ActivityThread.handleCreateService(ActivityThread.java:3883)
at android.app.ActivityThread.-wrap8(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1909)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:7325)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1230)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1120)
Caused by java.lang.IllegalStateException: WorkManager needs to be initialized via a ContentProvider#onCreate() or an Application#onCreate().
at androidx.work.impl.background.systemjob.SystemJobService.onCreate(SystemJobService.java:73)
at android.app.ActivityThread.handleCreateService(ActivityThread.java:3873)
at android.app.ActivityThread.-wrap8(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1909)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:7325)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1230)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1120)
as...@gmail.com <as...@gmail.com> #16
an...@google.com <an...@google.com> #17
ba...@gmail.com <ba...@gmail.com> #18
if (!Application.class.equals(getApplication().getClass())) {
// During auto-backup, we don't get a custom Application subclass. This code path
// indicates we are either performing auto-backup or the user never used a custom
// Application class (or both).
throw new IllegalStateException("WorkManager needs to be initialized via a "
+ "ContentProvider#onCreate() or an Application#onCreate().");
}
im...@gmail.com <im...@gmail.com> #19
this issue on workManager 2.5.0 when initialize WorkManager in background thread, bring it back to UI thread
an...@google.com <an...@google.com> #20
Stacktrace:
Caused by java.lang.IllegalStateException
WorkManager needs to be initialized via a ContentProvider#onCreate() or an Application#onCreate().
androidx.work.impl.background.systemjob.SystemJobService.onCreate (SystemJobService.java:73)
android.app.ActivityThread.handleCreateService (ActivityThread.java:3833)
android.app.ActivityThread.access$1500 (ActivityThread.java:229)
android.app.ActivityThread$H.handleMessage (ActivityThread.java:1881)
android.os.Handler.dispatchMessage (Handler.java:106)
android.os.Looper.loop (Looper.java:226)
android.app.ActivityThread.main (ActivityThread.java:7178)
java.lang.reflect.Method.invoke (Method.java)
com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:503)
com.android.internal.os.ZygoteInit.main (ZygoteInit.java:942)
For now, the device affected is
android 9 Realme.
Work Manager version: 2.6.0
im...@gmail.com <im...@gmail.com> #21
Still happening with WorkManager 2.7.1 Samsung Galaxy S21 Ultra 5G Android 12 (SDK 31)
java.lang.RuntimeException:
at android.app.ActivityThread.handleCreateService (ActivityThread.java:4953)
at android.app.ActivityThread.access$1900 (ActivityThread.java:310)
at android.app.ActivityThread$H.handleMessage (ActivityThread.java:2300)
at android.os.Handler.dispatchMessage (Handler.java:106)
at android.os.Looper.loopOnce (Looper.java:226)
at android.os.Looper.loop (Looper.java:313)
at android.app.ActivityThread.main (ActivityThread.java:8669)
at java.lang.reflect.Method.invoke (Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:571)
at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1135)
Caused by: java.lang.IllegalStateException:
at androidx.work.impl.WorkManagerImpl.getInstance (WorkManagerImpl.java:158)
at androidx.work.impl.foreground.SystemForegroundDispatcher.<init> (SystemForegroundDispatcher.java:105)
at androidx.work.impl.foreground.SystemForegroundService.initializeDispatcher (SystemForegroundService.java:96)
at androidx.work.impl.foreground.SystemForegroundService.onCreate (SystemForegroundService.java:59)
at android.app.ActivityThread.handleCreateService (ActivityThread.java:4940)
at android.app.ActivityThread.access$1900 (ActivityThread.java:310)
at android.app.ActivityThread$H.handleMessage (ActivityThread.java:2300)
at android.os.Handler.dispatchMessage (Handler.java:106)
at android.os.Looper.loopOnce (Looper.java:226)
at android.os.Looper.loop (Looper.java:313)
at android.app.ActivityThread.main (ActivityThread.java:8669)
at java.lang.reflect.Method.invoke (Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:571)
at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1135)
wh...@gmail.com <wh...@gmail.com> #22
We recently moved our WorkManager initialization to the default pattern and have started seeing this same issue.
java.lang.RuntimeException: Unable to create service androidx.work.impl.background.systemjob.SystemJobService: java.lang.IllegalStateException: WorkManager needs to be initialized via a ContentProvider#onCreate() or an Application#onCreate().
at android.app.ActivityThread.handleCreateService(ActivityThread.java:5073)
at android.app.ActivityThread.-$$Nest$mhandleCreateService(Unknown)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2425)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:226)
at android.os.Looper.loop(Looper.java:313)
at android.app.ActivityThread.main(ActivityThread.java:8741)
at java.lang.reflect.Method.invoke(Method.java:-2)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:571)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1067)
Caused by: java.lang.IllegalStateException: WorkManager needs to be initialized via a ContentProvider#onCreate() or an Application#onCreate().
at androidx.work.impl.background.systemjob.SystemJobService.onCreate(SystemJobService:73)
at android.app.ActivityThread.handleCreateService(ActivityThread.java:5060)
at android.app.ActivityThread.-$$Nest$mhandleCreateService(Unknown)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2425)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:226)
at android.os.Looper.loop(Looper.java:313)
at android.app.ActivityThread.main(ActivityThread.java:8741)
at java.lang.reflect.Method.invoke(Method.java:-2)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:571)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1067)
ro...@gmail.com <ro...@gmail.com> #23
I would check to make sure that the app is not a multi-process app. If it is, you will need your Application
to additionally sub-class Configuration.Provider
or additionally define a second provider for the other process.
ne...@gmail.com <ne...@gmail.com> #24
ke...@gmail.com <ke...@gmail.com> #25
su...@gmail.com <su...@gmail.com> #26
wh...@gmail.com <wh...@gmail.com> #27
If your project does not have a deep dependency on the paging3 library, I recommend implementing a simple paging library yourself to avoid all the weird features of paging3. Paging library needs the coordination of the view layer and the model layer. It doesn't make sense to encapsulate these together.
Here's an example from my project. If you don't need to modify the list, it can be implemented much simpler.
da...@gmail.com <da...@gmail.com> #28
al...@mercari.com <al...@mercari.com> #29
Still not fixed in latest versions, workaround works. Please consider re-opening the issue.
co...@gmail.com <co...@gmail.com> #30
jo...@kalderstam.se <jo...@kalderstam.se> #31
Seconded. Still a problem in 1.3.1
[Deleted User] <[Deleted User]> #32
In our case, it seems to be triggered by adding headers/footers to the `LazyColumn`.
Please consider re-opening the issue.
il...@google.com <il...@google.com> #33
The default case (all of your items are loaded asynchronously) was fixed in cachedIn
in your layer above Compose (e.g., your ViewModel layer).
However, the case where you have some items available immediately (like a header and footer) and some items available only asynchronously, is not fixed. This is broken whether you use Paging or not - any data loaded from a Flow
would suffer this same problem (as they also would not be available in the first composition).
Creating an generic API for informing the Lazy Layout of when all asynchronously loaded items have finished loading (particularly noting that there can be multiple items
blocks loading from multiple asynchronous sources) is not something we can solve in the short or medium term.
That being said, we believe there may be a way to solve this in the specific case of headers+footers and a single call to collectAsLazyPagingItems
without having to wait for an overhaul of either the Paging or Lazy layout APIs, so we will be reopening this issue to track that internal investigation.
bi...@gmail.com <bi...@gmail.com> #34
pa...@outlook.com <pa...@outlook.com> #35
Sounds like Paging and Component are being developed with a lot of various serious issues that are piling with each version, and the team does not have any intention of fixing them. That's alright, at least now we can finally stop waiting for this to be fixed and instead stop using them entirely. Good luck with this.
cl...@google.com <cl...@google.com> #36
Compose requires an initial state for valid first composition.
Paging 3's entire architecture is based around emitting data through a Flow that doesn't emit until collected.
Therefore, significant changes within Paging 3 are required to enable initial states in paging-compose without collection. While these changes are not easily resolvable, we hope to address them in the coming year.
au...@gmail.com <au...@gmail.com> #37
vi...@gmail.com <vi...@gmail.com> #39
ty...@gmail.com <ty...@gmail.com> #40
ap...@google.com <ap...@google.com> #41
Branch: androidx-main
commit 4a567a83c33a3f15556d4777e97327e418cbe42e
Author: Clara Fok <clarafok@google.com>
Date: Wed Apr 26 12:29:43 2023
Initialize PagingDataDiffer with cached data
PagingDataDiffer constructor now accepts a nullable PagingData containing a nullable cached PageEvent.Insert. PagePresenter and combinedLoadStatesCollection will be initialized wiht the cached data if present. This allows cached items and loadStates to be accessible immediately without requiring any collection.
Note that HintReciver and UiReceiver is not set, hence differ will not respond to any UI input (scrolling / refresh etc) until a real PagingData has been collected from.
Test: ./gradlew paging:paging-common:test
Bug: 177245496
Change-Id: I3a9a80486a1e345699745311dcb585efbf7939bd
M paging/paging-common/src/main/kotlin/androidx/paging/PagePresenter.kt
M paging/paging-common/src/main/kotlin/androidx/paging/PagingDataDiffer.kt
M paging/paging-common/src/test/kotlin/androidx/paging/PagingDataDifferTest.kt
ap...@google.com <ap...@google.com> #42
Branch: androidx-main
commit ac2a48843976a46cdf9eb6db1d4d08d59deecbd8
Author: Clara Fok <clarafok@google.com>
Date: Tue Apr 25 16:43:38 2023
Exposed cached data via PagingData
When using Flow<PagingData<T>>.cachedIn(scope), data that has been collected within that generation is cached for as long as the scope remains alive.
Normally the cachedData can only be accessed by new subscriptions when they collect on the SharedFlow returned by cachedIn. Now we expose this cachedData via PagingData so that the cached data can be accessed synchronously without requiring collection.
Test: ./gradlew paging:paging-common:test
Bug: 177245496
Change-Id: I36a894d7d90f61396604e7847c5be6027b5e0991
M paging/paging-common/src/main/kotlin/androidx/paging/CachedPageEventFlow.kt
M paging/paging-common/src/main/kotlin/androidx/paging/CachedPagingData.kt
M paging/paging-common/src/main/kotlin/androidx/paging/PagingData.kt
M paging/paging-common/src/test/kotlin/androidx/paging/CachingTest.kt
8h...@gmail.com <8h...@gmail.com> #43
ap...@google.com <ap...@google.com> #44
Branch: androidx-main
commit 75b22ef9f04fc4d615b75f65d904dd4cc765bffd
Author: Clara Fok <clarafok@google.com>
Date: Thu Apr 27 14:03:07 2023
Cached data immediately available in LazyPagingItems
Paged data can be cached via pager.flow.cachedIn(scope). If collecting LazyPagingItems from this cachedIn flow, any cached data will be made available for presentation upon #collectAsLazyPagingItems initialization without requiring asynchronous collection.
This means that upon configuration change or navigating away and back, instead of LazyPagingItems containing zero items, it will now contain previously loaded data that can be immediately presented within initial composition. This helps preserve scroll state and a smoother UI transition.
Note that this only applies to flows returned directly from pager.flow.cachedIn(scope), so mapping or transformations should be applied prior to cachedIn.
Test: ./gradlew paging:paging-compose:cC
Fixes: 177245496
Relnote: "Cached data from pager.flow.cachedIn that has been collected in LazyPagingItems will now be immediately available after state restoration without requiring asyncrhonous collection. This means the cached data will be ready for presentation immediately upon initial composition after state is restored."
Change-Id: I97a6078c0563f8017af24448c32e59b86a987465
M paging/paging-compose/src/androidTest/java/androidx/paging/compose/LazyPagingItemsTest.kt
M paging/paging-compose/src/main/java/androidx/paging/compose/LazyPagingItems.kt
cl...@google.com <cl...@google.com> #45
This has been fixed internally and will be available in the next release. The next release version is TBD.
In short, the fix works by making cached data immediately available for presentation after config change / navigation. This prevents the list from having zero items right after config change/ navigation so the scroll state will be preserved without requiring workarounds such as
That said, it only works if there is cached data to begin with. This means you need pager.flow.cachedIn(scope)
. Note that any PagedData mapping/transformations should be applied prior to cachedIn(), such as
val pager = pager.flow.map { pagingData ->
pagingData.map {
// map items
}
}.cachedIn(scope)
8h...@gmail.com <8h...@gmail.com> #46
Is it going to fix incorrect loadState
(Loading
) as well (to avoid displaying incorrect refresh indicator to users)?
val lazyPagingItems = viewModel.pagingDataExample.collectAsLazyPagingItems()
val loadState = lazyPagingItems.loadState.refresh // loadState == Loading initially on config changes/navigation
It should be NotLoading
if data is returned from cahce and no request to refresh is triggered
cl...@google.com <cl...@google.com> #47
Yes it will start with NotLoading
if displaying cachedData.
na...@google.com <na...@google.com> #48
The following release(s) address this bug.It is possible this bug has only been partially addressed:
androidx.paging:paging-common:3.2.0-alpha05
il...@google.com <il...@google.com> #49
I'll note that while some of the changes from this bug made it into
ju...@google.com <ju...@google.com> #50
The following release(s) address this bug.It is possible this bug has only been partially addressed:
androidx.paging:paging-compose:1.0.0-alpha20
so...@gmail.com <so...@gmail.com> #51
cl...@google.com <cl...@google.com> #52
paging-common 3.2.0-alpha06
?
il...@google.com <il...@google.com> #53
Re
so...@gmail.com <so...@gmail.com> #54
is...@gmail.com <is...@gmail.com> #55
Regarding the workaround from firstVisibleItemIndex
and firstVisibleItemScrollOffset
to continue to remain the same in case other code depends on them, it looks like the workaround should be modified to this?
@Composable
fun <T : Any> LazyPagingItems<T>.rememberLazyGridState(): LazyGridState {
val state = androidx.compose.foundation.lazy.grid.rememberLazyGridState()
return when (itemCount) {
// Return a different LazyListState instance.
0 -> remember(this) {
LazyGridState(
firstVisibleItemIndex = state.firstVisibleItemIndex,
firstVisibleItemScrollOffset = state.firstVisibleItemScrollOffset
)
}
// Return rememberLazyListState (normal case).
else -> state
}
}
h9...@gmail.com <h9...@gmail.com> #56
If there is a combined flow, cachedIn does not seem to work. Could you please confirm?
val pager = pager.flow
.map { pagingData ->
pagingData.map {
// map items
}
}
.cachedIn(scope)
.combine(otherFlow) { pagingData, otherFlow ->
pagingData.map{
// something
}
}
.cachedIn(scope)
If you call the pager again with collectAsLazyPagingItems()
, LoadState will be called LoadState.Loading
rather than LoadState.NotLoading
.
cl...@google.com <cl...@google.com> #57
Re
jo...@mozilla.com <jo...@mozilla.com> #58
The workarounds, like in
il...@google.com <il...@google.com> #59
Re
Please file a new issue with a sample project that reproduces your issue if you are seeing something different.
pa...@gmail.com <pa...@gmail.com> #60
im having a similar problem as
cl...@google.com <cl...@google.com> #61
Re
pr...@gmail.com <pr...@gmail.com> #62
@Composable
fun <T : Any> LazyPagingItems<T>.rememberLazyListState(): LazyListState {
val state = androidx.compose.foundation.lazy.rememberLazyListState()
val scrollIndex = rememberSaveable { mutableIntStateOf(0) }
val coroutineScope = rememberCoroutineScope()
// After recreation, LazyPagingItems first return 0 items, then the cached items.
// This behavior/issue is resetting the LazyListState scroll position.
return when (itemCount) {
// Return a different LazyListState instance.
0 -> rememberSaveable(saver = LazyListState.Saver) {
scrollIndex.intValue = state.firstVisibleItemIndex
LazyListState(state.firstVisibleItemIndex, state.firstVisibleItemScrollOffset)
}
else -> {
if(scrollIndex.intValue in 1..<itemCount) {
// Return state after scrolling.
LaunchedEffect("") {
coroutineScope.launch {
state.animateScrollToItem(scrollIndex.intValue, state.firstVisibleItemScrollOffset)
scrollIndex.intValue = 0
}
}
}
state
}
}
}
se...@gmail.com <se...@gmail.com> #63
I want to share my case with a positive result.
I use compose paging 3 (api + room), navigation.
This is my case in detail.
1. Screen A (list with api + room pagination)
2. Screen B (details + changing data in the database by item ID)
When I changed the data on screen B and returned to screen A, the position was reset.
The reason was as follows. When my LazyPagerItems<*> object was with ItemCount == 0, I used item {} to show placeholder. That was the reason! When I started using items even with ItemCount == 0, the position reset stopped happening.
Bad Case:
Lazy Column() {
when {
values == null || values.itemCount == 0 -> item {} //simple item
items () {} //for paging items
}
}
Nice Case:
Lazy Column() {
when {
values == null -> item {} //simple item
items (
count = values.itemCount,
) {} //for paging items
}
}
Then I started adding elements to LazyColumn and making it combined. In this case, it also reset the position! By removing the item at the beginning, the list began to work correctly.
Bad Case:
Lazy Column() {
item {} //simple item
item {} //simple item
items () {} //for paging items
}
Nice Case:
Lazy Column() {
items () {} //for paging items
}
to...@gmail.com <to...@gmail.com> #64
Yes they do not want the if and placeholders to be inside the LazyColumn we are supposed to use if outside of the LazyColum.
The workaround is to use a fake lazystate until your data is loaded.
See for example solution
cl...@google.com <cl...@google.com> #65
re cachedIn()
, you shouldn't need a workaround. Can you upload a sample app for me to take a look?
to...@gmail.com <to...@gmail.com> #66
Cachein does not keep the data when the app is killed in the background or viewmodel is unloaded.
sa...@gmail.com <sa...@gmail.com> #67
to...@gmail.com <to...@gmail.com> #68
That's cute but you might actually want to read the issue.
hu...@nevix.tech <hu...@nevix.tech> #69
val pager = pager.flow
.map { pagingData ->
pagingData.map {
// map items
}
}
.cachedIn(scope)
.combine(otherFlow) { pagingData, otherFlow ->
pagingData.map{
// something
}
}
.cachedIn(scope)
my-version = androidx.paging:paging-compose:3.3.5
When otherFlow updates data, an error occurs: Attempt to collect twice from pageEventFlow, which is an illegal operation. Did you forget to call Flow<PagingData<*>>.cachedIn(coroutineScope)?
Description
Jetpack Compose release version: 1.0.0-alpha09 Android Studio Build: Build #AI-202.7319.50.2031.7049475, built on December 24, 2020
Steps to Reproduce:
NavHost
-ed composable screen containing aLazyColumn
built withLazyPagingItems
collected from a view modelResult: the LazyColumn is scrolled back to top
Expected: the scroll position should be remembered
Sample code that works:https://gist.github.com/CyrilFind/5c7d108dd8d030fe7a6a556e43f29f6f
If the
.collectAsLazyPagingItems()
line from this sample is done before declaring theNavHost
, it works as expected but if it is done inside thecomposable
block, it does not.