Status Update
Comments
lp...@google.com <lp...@google.com> #2
Branch: androidx-master-dev
commit c60f33e229e31ab328ef6b59dab63b264954831c
Author: Alexandre Elias <aelias@google.com>
Date: Fri Jul 10 16:23:09 2020
Semantics no-op cleanups
Partly in response to lmr's broad code review, I did a pass of
superficial API/implementation cleanup. The main changes are:
- I changed each Boolean SemanticsProperty where false is equivalent to
not being present to take "Unit" instead. This is conceptually
clearer: it avoids questions like "can I cancel out a semantics from a
merged child by setting it to false?" Because "property = Unit" looks
weird, I also changed the style of these to "property()".
- I moved the Semantics id generator closer to where it's used, in
SemanticsModifierCore. I made it internal and an AtomicInt.
(Note that integer ids are heavily used in the Android
AccessibilityNodeInfo APIs so I can't simply remove them entirely.)
- I deleted dead code. Some examples include SemanticsHintOverrides,
a public API not connected to anything, and SemanticsPropertyKey
merge() open method which is never called. (In both cases I have
a different plan in mind for accessibility.)
Fixes: 145951226
Fixes: 145955412
Test: existing tests
Relnote: "Single-value semantics properties now use a calling style.
For example, 'semantics { hidden = true }' is now written as:
'semantics { hidden() }'."
Change-Id: Ic1afd12ea22c926babc9662f1804d80b33aa0cfc
M ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/LayoutNodeModifierBenchmark.kt
M ui/ui-core/api/0.1.0-dev15.txt
M ui/ui-core/api/current.txt
M ui/ui-core/api/public_plus_experimental_0.1.0-dev15.txt
M ui/ui-core/api/public_plus_experimental_current.txt
M ui/ui-core/api/restricted_0.1.0-dev15.txt
M ui/ui-core/api/restricted_current.txt
M ui/ui-core/src/androidAndroidTest/kotlin/androidx/ui/graphics/vector/VectorTest.kt
M ui/ui-core/src/androidAndroidTest/kotlin/androidx/ui/semantics/SemanticsTests.kt
M ui/ui-core/src/androidMain/kotlin/androidx/ui/core/AndroidActuals.kt
M ui/ui-core/src/androidMain/kotlin/androidx/ui/core/AndroidComposeView.kt
M ui/ui-core/src/androidMain/kotlin/androidx/ui/core/AndroidComposeViewAccessibilityDelegateCompat.kt
M ui/ui-core/src/androidMain/kotlin/androidx/ui/core/AndroidPopup.kt
M ui/ui-core/src/commonMain/kotlin/androidx/ui/core/Expect.kt
M ui/ui-core/src/commonMain/kotlin/androidx/ui/core/semantics/SemanticsConfiguration.kt
D ui/ui-core/src/commonMain/kotlin/androidx/ui/core/semantics/SemanticsHintOverrides.kt
M ui/ui-core/src/commonMain/kotlin/androidx/ui/core/semantics/SemanticsModifier.kt
M ui/ui-core/src/commonMain/kotlin/androidx/ui/core/semantics/SemanticsNode.kt
M ui/ui-core/src/commonMain/kotlin/androidx/ui/core/semantics/SemanticsOwner.kt
M ui/ui-core/src/commonMain/kotlin/androidx/ui/core/semantics/SemanticsWrapper.kt
M ui/ui-core/src/commonMain/kotlin/androidx/ui/semantics/SemanticsProperties.kt
M ui/ui-foundation/api/0.1.0-dev15.txt
M ui/ui-foundation/api/current.txt
M ui/ui-foundation/api/public_plus_experimental_0.1.0-dev15.txt
M ui/ui-foundation/api/public_plus_experimental_current.txt
M ui/ui-foundation/api/restricted_0.1.0-dev15.txt
M ui/ui-foundation/api/restricted_current.txt
M ui/ui-foundation/src/main/java/androidx/ui/foundation/Clickable.kt
M ui/ui-foundation/src/main/java/androidx/ui/foundation/Dialog.kt
M ui/ui-foundation/src/main/java/androidx/ui/foundation/Scroller.kt
M ui/ui-foundation/src/main/java/androidx/ui/foundation/selection/Selectable.kt
M ui/ui-foundation/src/main/java/androidx/ui/foundation/selection/Toggleable.kt
M ui/ui-foundation/src/main/java/androidx/ui/foundation/semantics/FoundationSemanticsProperties.kt
M ui/ui-material/src/androidTest/java/androidx/ui/material/ButtonTest.kt
M ui/ui-material/src/androidTest/java/androidx/ui/material/CardTest.kt
M ui/ui-material/src/androidTest/java/androidx/ui/material/CheckboxScreenshotTest.kt
M ui/ui-material/src/androidTest/java/androidx/ui/material/RadioButtonScreenshotTest.kt
M ui/ui-material/src/androidTest/java/androidx/ui/material/ScaffoldTest.kt
M ui/ui-material/src/androidTest/java/androidx/ui/material/SnackbarTest.kt
M ui/ui-material/src/androidTest/java/androidx/ui/material/SurfaceTest.kt
M ui/ui-material/src/androidTest/java/androidx/ui/material/ripple/RippleIndicationTest.kt
M ui/ui-material/src/androidTest/java/androidx/ui/material/textfield/TextFieldScreenshotTest.kt
M ui/ui-material/src/main/java/androidx/ui/material/AppBar.kt
M ui/ui-material/src/main/java/androidx/ui/material/TextFieldImpl.kt
M ui/ui-test/src/androidTest/java/androidx/ui/test/AssertsTest.kt
M ui/ui-test/src/androidTest/java/androidx/ui/test/CallSemanticsActionTest.kt
M ui/ui-test/src/androidTest/java/androidx/ui/test/ErrorMessagesTest.kt
M ui/ui-test/src/androidTest/java/androidx/ui/test/FindersTest.kt
M ui/ui-test/src/androidTest/java/androidx/ui/test/PrintToStringTest.kt
M ui/ui-test/src/androidTest/java/androidx/ui/test/ScrollToTest.kt
M ui/ui-test/src/androidTest/java/androidx/ui/test/TextActionsTest.kt
M ui/ui-test/src/main/java/androidx/ui/test/Actions.kt
M ui/ui-test/src/main/java/androidx/ui/test/Filters.kt
M ui/ui-text/api/0.1.0-dev15.txt
M ui/ui-text/api/current.txt
M ui/ui-text/api/public_plus_experimental_0.1.0-dev15.txt
M ui/ui-text/api/public_plus_experimental_current.txt
M ui/ui-text/api/restricted_0.1.0-dev15.txt
M ui/ui-text/api/restricted_current.txt
M ui/ui-text/src/commonMain/kotlin/androidx/ui/text/CoreTextField.kt
M ui/ui-text/src/commonMain/kotlin/androidx/ui/text/TextSemanticsProperties.kt
lp...@google.com <lp...@google.com> #3
if (transition.currentState == transition.targetState) {
Fundamentally this is dangerous / incorrect. At the minimum, this should be inside a SideEffect / LaunchedEffect, so that it happens after composition (and after composition was successful). We should only be making changes that invalidate something as a result of composition, not inside composition.
That said, this might not fix the issue (although we should still do it regardless): there are some other problems with subcomposition in specific scenarios that we might run into, since it happens in the measure phase, after all non-subcompose things have been fully composed. See
ti...@google.com <ti...@google.com> #4
Agreed that the change in if(transition.currentState == transition.targetState)
should be in an effect, unless it's backed by snapshot and therefore can be rolled back if the composition fails.
This is unlikely the cause for the issue, because composition failing was theorectical and wasn't enabled last time I checked. If we could reliably repro the crash, it's more likely to be something else. With that said, the root cause for this may be the same as
Jeremy, could you log how transition.currentState
and transition.targetState
over time so we know whether 1) the animation has finished and removed the Scalfold, or 2) AnimatedContent
failed to pick up any animation and finished within one frame?
jb...@google.com <jb...@google.com> #5
Added a sample that has some of external logging. We can see that the state of the entry changes from outside the Scaffold to inside the scaffold. Gonna add the request logging and follow up with the logs here as well.
jb...@google.com <jb...@google.com> #6
The additional logging added internal to AnimatedNavHost:
currentEntry?.LocalOwnersProvider(saveableStateHolder) {
Log.d("AnimatedNavHost", "Current entry being composed in AnimatedContent ${currentEntry.destination.route}")
(currentEntry.destination as AnimatedComposeNavigator.Destination)
.content(this, currentEntry)
}
}
Log.d("AnimatedNavHost", "Outside of conditional")
Log.d("AnimatedNavHost", "transition currentState: ${transition.currentState.destination.route}")
Log.d("AnimatedNavHost", "transition targetState: ${transition.targetState.destination.route}")
if (transition.currentState == transition.targetState) {
Log.d("AnimatedNavHost", "Inside of conditional")
Log.d("AnimatedNavHost", "transition currentState: ${transition.currentState.destination.route}")
Log.d("AnimatedNavHost", "transition targetState: ${transition.targetState.destination.route}")
visibleEntries.forEach { entry ->
composeNavigator.markTransitionComplete(entry)
}
}
The resulting logs:
E screen2 entry state in Scaffold RESUMED
D navigating to screen1
D Current entry being composed in AnimatedContent screen2
E screen2 entry state outside Scaffold is CREATED
D Current entry being composed in AnimatedContent screen1
D Outside of conditional
D transition currentState: screen2
D transition targetState: screen1
D Current entry being composed in AnimatedContent screen1
D Outside of conditional
D transition currentState: screen1
D transition targetState: screen1
D Inside of conditional
D transition currentState: screen1
D transition targetState: screen1
E screen2 entry state in Scaffold DESTROYED
E FATAL EXCEPTION: main
Process: com.andreirozov.sample, PID: 6388
java.lang.IllegalStateException: You cannot access the NavBackStackEntry's ViewModels after the NavBackStackEntry is destroyed.
ti...@google.com <ti...@google.com> #7
Thanks for the logs Jeremy.
Seems like AnimatedContent didn't pick up any animation from Scaffold. I see the 2 invocations of AnimatedNavHost
, the first one sets up the new target state, and by the 2nd invocation the transition has already considered itself finished, which is likely due to not having any child animations in the list. That seems to me like the scenario 2) as I mentioned in
1st invocation:
E screen2 entry state in Scaffold RESUMED
D navigating to screen1
D Current entry being composed in AnimatedContent screen2
E screen2 entry state outside Scaffold is CREATED
D Current entry being composed in AnimatedContent screen1
D Outside of conditional
D transition currentState: screen2
D transition targetState: screen1
2nd invocation:
D Current entry being composed in AnimatedContent screen1
D Outside of conditional
D transition currentState: screen1
D transition targetState: screen1
D Inside of conditional
D transition currentState: screen1
D transition targetState: screen1
E screen2 entry state in Scaffold DESTROYED
E FATAL EXCEPTION: main
ti...@google.com <ti...@google.com> #8
Jeremy, could you share the steps to reproduce the issue in the app shared in
jb...@google.com <jb...@google.com> #9
Pressing the menu and swapping between screen1 and screen2 as seen in
It fails very quickly on API 29 from my testing, on higher API levels it takes a few times.
ti...@google.com <ti...@google.com> #10
Thanks for sharing the repro steps. I was able to repro with a fair amount of rapid switching between destinations.
Here's the stacktrace I see. Since I don't have the logging in AnimatedHost
, I'm not sure whether this is the same crash as #6.
Jeremy, it'd be helpful if you could post the full stacktrace with the logging from AnimatedHost, so I can compare the composable involved in the crash with the destination that was removed.
2023-02-15 13:23:25.057 31458-31458 AndroidRuntime com.andreirozov.sample E FATAL EXCEPTION: main
Process: com.andreirozov.sample, PID: 31458
java.lang.IllegalStateException: You cannot access the NavBackStackEntry's ViewModels after the NavBackStackEntry is destroyed.
at androidx.navigation.NavBackStackEntry.getViewModelStore(NavBackStackEntry.kt:202)
at androidx.lifecycle.viewmodel.compose.ViewModelKt.get(ViewModel.kt:206)
at androidx.lifecycle.viewmodel.compose.ViewModelKt.get$default(ViewModel.kt:195)
at androidx.lifecycle.viewmodel.compose.ViewModelKt.viewModel(ViewModel.kt:120)
at com.andreirozov.sample.ui.standings.screen2.Screen2TabKt.AnotherScreen(Screen2Tab.kt:84)
at com.andreirozov.sample.ui.AppNavGraphKt$AppNavGraph$5$1$1$2.invoke(AppNavGraph.kt:94)
at com.andreirozov.sample.ui.AppNavGraphKt$AppNavGraph$5$1$1$2.invoke(AppNavGraph.kt:89)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:116)
at androidx.compose.runtime.internal.ComposableLambdaImpl$invoke$1.invoke(ComposableLambda.jvm.kt:127)
at androidx.compose.runtime.internal.ComposableLambdaImpl$invoke$1.invoke(ComposableLambda.jvm.kt:127)
at androidx.compose.runtime.RecomposeScopeImpl.compose(RecomposeScopeImpl.kt:162)
at androidx.compose.runtime.ComposerImpl.recomposeToGroupEnd(Composer.kt:2435)
at androidx.compose.runtime.ComposerImpl.skipCurrentGroup(Composer.kt:2703)
at androidx.compose.runtime.ComposerImpl$doCompose$2$5.invoke(Composer.kt:3326)
at androidx.compose.runtime.ComposerImpl$doCompose$2$5.invoke(Composer.kt:3304)
at androidx.compose.runtime.SnapshotStateKt__DerivedStateKt.observeDerivedStateRecalculations(DerivedState.kt:341)
at androidx.compose.runtime.SnapshotStateKt.observeDerivedStateRecalculations(Unknown Source:1)
at androidx.compose.runtime.ComposerImpl.doCompose(Composer.kt:3304)
at androidx.compose.runtime.ComposerImpl.recompose$runtime_release(Composer.kt:3269)
at androidx.compose.runtime.CompositionImpl.recompose(Composition.kt:771)
at androidx.compose.runtime.Recomposer.performRecompose(Recomposer.kt:1047)
at androidx.compose.runtime.Recomposer.access$performRecompose(Recomposer.kt:124)
at androidx.compose.runtime.Recomposer$runRecomposeAndApplyChanges$2$2.invoke(Recomposer.kt:541)
at androidx.compose.runtime.Recomposer$runRecomposeAndApplyChanges$2$2.invoke(Recomposer.kt:510)
at androidx.compose.ui.platform.AndroidUiFrameClock$withFrameNanos$2$callback$1.doFrame(AndroidUiFrameClock.android.kt:34)
at androidx.compose.ui.platform.AndroidUiDispatcher.performFrameDispatch(AndroidUiDispatcher.android.kt:109)
at androidx.compose.ui.platform.AndroidUiDispatcher.access$performFrameDispatch(AndroidUiDispatcher.android.kt:41)
at androidx.compose.ui.platform.AndroidUiDispatcher$dispatchCallback$1.doFrame(AndroidUiDispatcher.android.kt:69)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1229)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1239)
at android.view.Choreographer.doCallbacks(Choreographer.java:899)
at android.view.Choreographer.doFrame(Choreographer.java:827)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1214)
at android.os.Handler.handleCallback(Handler.java:942)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loopOnce(Looper.java:201)
at android.os.Looper.loop(Looper.java:288)
at android.app.ActivityThread.main(ActivityThread.java:7898)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936)
Suppressed: kotlinx.coroutines.DiagnosticCoroutineContextException: [androidx.compose.runtime.PausableMonotonicFrameClock@9d17257, androidx.compose.ui.platform.MotionDurationScaleImpl@54e6144, StandaloneCoroutine{Cancelling}@29a522d, AndroidUiDispatcher@c8ce362]
ti...@google.com <ti...@google.com> #11
The log prior to the crash indicates Screen2's Scaffold recomposed after the BackStackEntry
's lifecyle has been updated to DESTROYED, presumably from markTransitionComplete
, which happens after AnimatedContent remove Screen2 from composition. Recomposing after being removed from composition looks like the same issue as
com.andreirozov.sample D navigating to screen1
com.andreirozov.sample E Standing back stack entry state is CREATED
com.andreirozov.sample E Standing back stack entry state is CREATED
com.andreirozov.sample E Standing back stack entry state in Scaffold CREATED
com.andreirozov.sample E Standing back stack entry state is CREATED
com.andreirozov.sample E Standing back stack entry state in Scaffold CREATED
com.andreirozov.sample D navigating to screen2
com.andreirozov.sample E Standing back stack entry state is STARTED
com.andreirozov.sample E Standing back stack entry state in Scaffold STARTED
com.andreirozov.sample E Standing back stack entry state is STARTED
com.andreirozov.sample E Standing back stack entry state in Scaffold STARTED
com.andreirozov.sample E Standing back stack entry state is STARTED
com.andreirozov.sample E Standing back stack entry state is RESUMED
com.andreirozov.sample E Standing back stack entry state in Scaffold RESUMED
com.andreirozov.sample E Standing back stack entry state is RESUMED
com.andreirozov.sample E Standing back stack entry state in Scaffold RESUMED
com.andreirozov.sample E Standing back stack entry state is RESUMED
com.andreirozov.sample E Standing back stack entry state in Scaffold RESUMED
com.andreirozov.sample E Standing back stack entry state is RESUMED
com.andreirozov.sample E Standing back stack entry state in Scaffold RESUMED
com.andreirozov.sample D navigating to screen1
com.andreirozov.sample E Standing back stack entry state is CREATED
com.andreirozov.sample E Standing back stack entry state is CREATED
com.andreirozov.sample E Standing back stack entry state in Scaffold CREATED
com.andreirozov.sample E Standing back stack entry state is CREATED
com.andreirozov.sample E Standing back stack entry state in Scaffold DESTROYED
com.andreirozov.sample E FATAL EXCEPTION: main
jb...@google.com <jb...@google.com> #12
Yep same one:
D Current entry being composed in AnimatedContent screen1
D Outside of conditional
D transition currentState: screen1
D transition targetState: screen1
D Inside of conditional
D transition currentState: screen1
D transition targetState: screen1
I Skipped 40 frames! The application may be doing too much work on its main thread.
D Installing profile for com.andreirozov.sample
D Current entry being composed in AnimatedContent screen1
D Outside of conditional
D transition currentState: screen1
D transition targetState: screen1
D Inside of conditional
D transition currentState: screen1
D transition targetState: screen1
D Current entry being composed in AnimatedContent screen1
D Outside of conditional
D transition currentState: screen1
D transition targetState: screen1
D Inside of conditional
D transition currentState: screen1
D transition targetState: screen1
D navigating to screen2
D Current entry being composed in AnimatedContent screen1
D Current entry being composed in AnimatedContent screen2
E screen2 entry state outside Scaffold is STARTED
D Outside of conditional
D transition currentState: screen1
D transition targetState: screen2
E screen2 entry state in Scaffold STARTED
D Current entry being composed in AnimatedContent screen2
E screen2 entry state outside Scaffold is STARTED
D Outside of conditional
D transition currentState: screen2
D transition targetState: screen2
D Inside of conditional
D transition currentState: screen2
D transition targetState: screen2
D Current entry being composed in AnimatedContent screen2
E screen2 entry state outside Scaffold is RESUMED
D Outside of conditional
D transition currentState: screen2
D transition targetState: screen2
D Inside of conditional
D transition currentState: screen2
D transition targetState: screen2
E screen2 entry state in Scaffold RESUMED
D Current entry being composed in AnimatedContent screen2
E screen2 entry state outside Scaffold is RESUMED
D Outside of conditional
D transition currentState: screen2
D transition targetState: screen2
D Inside of conditional
D transition currentState: screen2
D transition targetState: screen2
D Current entry being composed in AnimatedContent screen2
E screen2 entry state outside Scaffold is RESUMED
D Outside of conditional
D transition currentState: screen2
D transition targetState: screen2
D Inside of conditional
D transition currentState: screen2
D transition targetState: screen2
E screen2 entry state in Scaffold RESUMED
D navigating to screen1
D Current entry being composed in AnimatedContent screen2
E screen2 entry state outside Scaffold is CREATED
D Current entry being composed in AnimatedContent screen1
D Outside of conditional
D transition currentState: screen2
D transition targetState: screen1
I Background young concurrent copying GC freed 53694(1896KB) AllocSpace objects, 0(0B) LOS objects, 25% free, 4767KB/6441KB, paused 7.336ms total 70.605ms
D Current entry being composed in AnimatedContent screen1
D Outside of conditional
D transition currentState: screen1
D transition targetState: screen1
D Inside of conditional
D transition currentState: screen1
D transition targetState: screen1
E screen2 entry state in Scaffold DESTROYED
E FATAL EXCEPTION: main
Process: com.andreirozov.sample, PID: 2772
java.lang.IllegalStateException: You cannot access the NavBackStackEntry's ViewModels after the NavBackStackEntry is destroyed.
at androidx.navigation.NavBackStackEntry.getViewModelStore(NavBackStackEntry.kt:202)
at androidx.lifecycle.viewmodel.compose.ViewModelKt.get(ViewModel.kt:206)
at androidx.lifecycle.viewmodel.compose.ViewModelKt.get$default(ViewModel.kt:195)
at androidx.lifecycle.viewmodel.compose.ViewModelKt.viewModel(ViewModel.kt:120)
at com.andreirozov.sample.ui.standings.screen2.Screen2TabKt.AnotherScreen(Screen2Tab.kt:84)
at com.andreirozov.sample.ui.AppNavGraphKt$AppNavGraph$5$1$1$2.invoke(AppNavGraph.kt:94)
at com.andreirozov.sample.ui.AppNavGraphKt$AppNavGraph$5$1$1$2.invoke(AppNavGraph.kt:89)
at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:116)
at androidx.compose.runtime.internal.ComposableLambdaImpl$invoke$1.invoke(ComposableLambda.jvm.kt:127)
at androidx.compose.runtime.internal.ComposableLambdaImpl$invoke$1.invoke(ComposableLambda.jvm.kt:127)
at androidx.compose.runtime.RecomposeScopeImpl.compose(RecomposeScopeImpl.kt:162)
at androidx.compose.runtime.ComposerImpl.recomposeToGroupEnd(Composer.kt:2435)
at androidx.compose.runtime.ComposerImpl.skipCurrentGroup(Composer.kt:2703)
at androidx.compose.runtime.ComposerImpl$doCompose$2$5.invoke(Composer.kt:3326)
at androidx.compose.runtime.ComposerImpl$doCompose$2$5.invoke(Composer.kt:3304)
at androidx.compose.runtime.SnapshotStateKt__DerivedStateKt.observeDerivedStateRecalculations(DerivedState.kt:341)
at androidx.compose.runtime.SnapshotStateKt.observeDerivedStateRecalculations(Unknown Source:1)
at androidx.compose.runtime.ComposerImpl.doCompose(Composer.kt:3304)
at androidx.compose.runtime.ComposerImpl.recompose$runtime_release(Composer.kt:3269)
at androidx.compose.runtime.CompositionImpl.recompose(Composition.kt:771)
at androidx.compose.runtime.Recomposer.performRecompose(Recomposer.kt:1047)
at androidx.compose.runtime.Recomposer.access$performRecompose(Recomposer.kt:124)
at androidx.compose.runtime.Recomposer$runRecomposeAndApplyChanges$2$2.invoke(Recomposer.kt:541)
at androidx.compose.runtime.Recomposer$runRecomposeAndApplyChanges$2$2.invoke(Recomposer.kt:510)
at androidx.compose.ui.platform.AndroidUiFrameClock$withFrameNanos$2$callback$1.doFrame(AndroidUiFrameClock.android.kt:34)
at androidx.compose.ui.platform.AndroidUiDispatcher.performFrameDispatch(AndroidUiDispatcher.android.kt:109)
at androidx.compose.ui.platform.AndroidUiDispatcher.access$performFrameDispatch(AndroidUiDispatcher.android.kt:41)
at androidx.compose.ui.platform.AndroidUiDispatcher$dispatchCallback$1.doFrame(AndroidUiDispatcher.android.kt:69)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:964)
at android.view.Choreographer.doCallbacks(Choreographer.java:790)
at android.view.Choreographer.doFrame(Choreographer.java:721)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:951)
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)
Suppressed: kotlinx.coroutines.DiagnosticCoroutineContextException: [androidx.compose.runtime.PausableMonotonicFrameClock@7ebb51b, androidx.compose.ui.platform.MotionDurationScaleImpl@e6c86b8, StandaloneCoroutine{Cancelling}@f919c91, AndroidUiDispatcher@7da07f6]
I Sending signal. PID: 2772 SIG: 9
ti...@google.com <ti...@google.com> #13
Thanks Jeremy.
The logging in #12 also confirms Scaffold's behavior of recomposing after being removed from composition. Marking this a dup of
mo...@gmail.com <mo...@gmail.com> #14
ap...@google.com <ap...@google.com> #15
Branch: androidx-main
commit aeb4b9fc4ddeca49fcb2812ce828cc7be11beb30
Author: Jeremy Woods <jbwoods@google.com>
Date: Tue Aug 08 17:15:11 2023
Fix issue with changing state as part of composition
We should not change state as part of composition as that can cause
inconsistent behavior when reading state. We should instead only ever
change state in an Effect.
RelNote: "Fixed an issues in Navigation with Compose where when using a
Scaffold it was possible to get an error trying to access a DESTROYED
ViewModel"
Test: Added test in NavHostTest
Bug: 268422136
Change-Id: I1dc115fc2b454d68c1a23c78e72707d6d4c739a5
M navigation/navigation-compose/src/androidTest/java/androidx/navigation/compose/NavHostTest.kt
M navigation/navigation-compose/src/main/java/androidx/navigation/compose/NavHost.kt
na...@google.com <na...@google.com> #16
The following release(s) address this bug.It is possible this bug has only been partially addressed:
androidx.navigation:navigation-compose:2.8.0-alpha01
pa...@gmail.com <pa...@gmail.com> #17
Hello,
these changes in fact introduced in 2.7.1
are creating issues in our architecture. Basically we are no longer able to receive Lifecycle.Event.ON_DESTROY
as it is now triggered after onDispose
where we are unsubscribing a lifecycle observer. (Sample attached)
fun attachLifeCycleObserver(onDestroy:() -> Unit) {
val lifecycle = LocalLifecycleOwner.current.lifecycle
DisposableEffect(lifecycle) {
val eventObserver = LifecycleEventObserver { source, event ->
println("PACMAC -- event:${event.name} targetState:${event.targetState.name} source:${source.toString()}")
if (event == Lifecycle.Event.ON_DESTROY) {
onDestroy()
}
}
lifecycle.addObserver(eventObserver)
onDispose {
// We unsubscribe here but since 2.7.1 we are no longer receiving ON_DESTROY as it is actually triggered after onDispose
lifecycle.removeObserver(eventObserver)
}
}
}
We are using onDestroy
to run some cleanup but only if the nav destination is popped of the backstack.
Description
This is fromhttps://github.com/google/accompanist/issues/1487 :
I haven't been able to reproduce it without a lot of experimental APIs along with Navigation. But essentially given the following:
There are cases where the areas end up being composed in this order:
1, 3, 2
withAnimatedContent
completing the transition and then the Scaffold composing its content.The expectation was that it would always be
1, 2, 3
with the composition of theScaffold
content completing before the callback to area 3.Is the expectation correct? Or does
AnimatedContent
completion have some priority over theScaffold
?This could also be something going on in
Scaffold
, just wondering what the expectations are here.