Status Update
Comments
da...@gmail.com <da...@gmail.com> #2
However, it probably should not crash this way. Either it should work (with a never-changing value) or it should fail with a "are you *really* sure you want this here?" sort of error.
co...@gmail.com <co...@gmail.com> #3
The extension function is avoidable by just having the `get()`-getter function inside the class itself. That also avoids the crash.
cl...@google.com <cl...@google.com>
an...@google.com <an...@google.com> #4
@Model
data class AgreementViewState(var terms: Boolean, var privacy: Boolean) {
val canProceed get() = terms && privacy
}
(note the addtion of get()) which seems to be what you intended. `val canProceed = terms && privacy` is not a derived property, rather, it is an initialized property.
da...@gmail.com <da...@gmail.com> #5
I'll still argue that the initialized val shouldn't blow up the way that it does, though.
co...@gmail.com <co...@gmail.com> #6
It is generally advised in the Kotlin community to "limit mutability" and use 'val' as much as possible (book Effective Kotlin by Marcin Moskala), while here you advise all @Model data to use var fields?
Epoxy library works great with immutable models, and always properly rebinds existing views based on new immutable Model objects you pass into it.
an...@google.com <an...@google.com> #7
Branch: androidx-master-dev
commit fcf76b3241844f18106d6e850004952046d4bfb6
Author: Leland Richardson <lelandr@google.com>
Date: Wed May 13 16:58:59 2020
Deprecate @Model
Relnote: “
@Model annotation is now deprecated. Use state and mutableStateOf as alternatives. This deprecation decision was reached after much careful discussion.
Justification
=============
Rationale includes but is not limited to:
- Reduces API surface area and concepts we need to teach
- More closely aligns with other comparable toolkits (Swift UI, React, Flutter)
- Reversible decision. We can always bring @Model back later.
- Removes corner-case usage and difficult to answer questions about configuring @Model as things we need to handle
- @Model data classes, equals, hashcode, etc.
- How do I have some properties “observed” and others not?
- How do I specify structural vs. referential equality to be used in observation?
- Reduces “magic” in the system. Would reduce the likelihood of someone assuming system was smarter than it is (ie, it knowing how to diff a list)
- Makes the granularity of observation more intuitive.
- Improves refactorability from variable -> property on class
- Potentially opens up possibilities to do hand-crafted State-specific optimizations
- More closely aligns with the rest of the ecosystem and reduces ambiguity towards immutable or us “embracing mutable state”
Migration Notes
===============
Almost all existing usages of @Model are fairly trivially transformed in one of two ways. The example below has a @Model class with two properties just for the sake of example, and has it being used in a composable.
```
@Model class Position(
var x: Int,
var y: Int
)
@Composable fun Example() {
var p = remember { Position(0, 0) }
PositionChanger(
position=p,
onXChange={ p.x = it }
onYChange={ p.y = it }
)
}
```
Alternative 1: Use State<OriginalClass> and create copies.
----------------------------------------------------------
This approach is made easier with Kotlin’s data classes. Essentially, make all previously `var` properties into `val` properties of a data class, and then use `state` instead of `remember`, and assign the state value to cloned copies of the original using the data class `copy(...)` convenience method.
It’s important to note that this approach only works when the only mutations to that class were done in the same scope that the `State` instance is created. If the class is internally mutating itself outside of the scope of usage, and you are relying on the observation of that, then the next approach is the one you will want to use.
```
data class Position(
val x: Int,
val y: Int
)
@Composable fun Example() {
var p by state { Position(0, 0) }
PositionChanger(
position=p,
onXChange={ p = p.copy(x=it) }
onYChange={ p = p.copy(y=it) }
)
}
```
Alternative 2: Use mutableStateOf and property delegates
--------------------------------------------------------
This approach is made easier with Kotlin’s property delegates and the `mutableStateOf` API which allows you to create MutableState instances outside of composition. Essentially, replace all `var` properties of the original class with `var` properties with `mutableStateOf` as their property delegate. This has the advantage that the usage of the class will not change at all, only the internal implementation of it. The behavior is not completely identical to the original example though, as each property is now observed/subscribed to individually, so the recompositions you see after this refactor could be more narrow (a good thing).
```
class Position(x: Int, y: Int) {
var x by mutableStateOf(x)
var y by mutableStateOf(y)
}
// source of Example is identical to original
@Composable fun Example() {
var p = remember { Position(0, 0) }
PositionChanger(
position=p,
onXChange={ p.x = it }
onYChange={ p.y = it }
)
}
```
“
Bug: 156546430
Bug: 152993135
Bug: 152050010
Bug: 148866188
Bug: 148422703
Bug: 148394427
Bug: 146362815
Bug: 146342522
Bug: 143413369
Bug: 135715219
Bug: 126418732
Bug: 147088098
Bug: 143263925
Bug: 139653744
Change-Id: I409e8c158841eae1dd548b33f1ec80bb609cba31
M compose/compose-compiler-hosted/integration-tests/src/test/java/androidx/compose/plugins/kotlin/frames/FrameDiagnosticTests.kt
M compose/compose-runtime/api/0.1.0-dev12.txt
M compose/compose-runtime/api/current.txt
M compose/compose-runtime/api/public_plus_experimental_0.1.0-dev12.txt
M compose/compose-runtime/api/public_plus_experimental_current.txt
M compose/compose-runtime/api/restricted_0.1.0-dev12.txt
M compose/compose-runtime/api/restricted_current.txt
M compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/ComposeBenchmark.kt
M compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/SiblingBenchmark.kt
M compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/dbmonster/DbMonster.kt
M compose/compose-runtime/compose-runtime-benchmark/src/androidTest/java/androidx/compose/benchmark/realworld4/RealWorld4_DataModels.kt
M compose/compose-runtime/samples/src/main/java/androidx/compose/samples/ModelSamples.kt
M compose/compose-runtime/src/commonMain/kotlin/androidx/compose/FrameManager.kt
M compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Model.kt
M compose/compose-runtime/src/commonMain/kotlin/androidx/compose/MutableState.kt
M compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Observe.kt
M compose/compose-runtime/src/commonMain/kotlin/androidx/compose/Recompose.kt
M compose/compose-runtime/src/commonMain/kotlin/androidx/compose/StableMarker.kt
M ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/ModelObserverBenchmark.kt
M ui/integration-tests/benchmark/src/androidTest/java/androidx/ui/benchmark/test/RadioGroupBenchmark.kt
M ui/integration-tests/demos/src/main/java/androidx/ui/demos/DemoColorPalette.kt
M ui/integration-tests/src/main/java/androidx/ui/integration/test/foundation/RectsInColumnSharedModelTestCase.kt
M ui/ui-android-view/integration-tests/android-view-demos/src/main/java/androidx/ui/androidview/demos/WebComponentActivity.kt
M ui/ui-animation/src/main/java/androidx/ui/animation/AnimatedValueEffects.kt
M ui/ui-animation/src/main/java/androidx/ui/animation/Transition.kt
M ui/ui-core/src/androidTest/java/androidx/ui/core/test/AndroidLayoutDrawTest.kt
M ui/ui-core/src/androidTest/java/androidx/ui/core/test/AndroidViewCompatTest.kt
M ui/ui-core/src/androidTest/java/androidx/ui/core/test/ClipTest.kt
M ui/ui-core/src/androidTest/java/androidx/ui/core/test/DrawShadowTest.kt
M ui/ui-core/src/androidTest/java/androidx/ui/core/test/ModelReadsTest.kt
M ui/ui-core/src/androidTest/java/androidx/ui/core/test/OpacityTest.kt
D ui/ui-core/src/androidTest/java/androidx/ui/core/test/ValueModel.kt
M ui/ui-core/src/androidTest/java/androidx/ui/core/test/WithConstraintsTest.kt
M ui/ui-core/src/androidTest/java/androidx/ui/res/ResourcesTest.kt
M ui/ui-core/src/androidTest/java/androidx/ui/semantics/SemanticsTests.kt
M ui/ui-core/src/main/java/androidx/ui/core/ModelObserver.kt
M ui/ui-core/src/main/java/androidx/ui/res/Resources.kt
M ui/ui-core/src/test/java/androidx/ui/core/selection/SelectionManagerDragTest.kt
M ui/ui-core/src/test/java/androidx/ui/core/selection/SelectionManagerLongPressDragTest.kt
M ui/ui-core/src/test/java/androidx/ui/core/selection/SelectionManagerTest.kt
M ui/ui-foundation/src/androidTest/java/androidx/ui/foundation/DeterminateProgressTest.kt
M ui/ui-layout/src/androidTest/java/androidx/ui/layout/test/ContainerTest.kt
M ui/ui-layout/src/androidTest/java/androidx/ui/layout/test/OnPositionedTest.kt
M ui/ui-material/api/0.1.0-dev12.txt
M ui/ui-material/api/current.txt
M ui/ui-material/api/public_plus_experimental_0.1.0-dev12.txt
M ui/ui-material/api/public_plus_experimental_current.txt
M ui/ui-material/api/restricted_0.1.0-dev12.txt
M ui/ui-material/api/restricted_current.txt
M ui/ui-material/integration-tests/material-demos/src/main/java/androidx/ui/material/demos/DynamicThemeActivity.kt
M ui/ui-material/src/androidTest/java/androidx/ui/material/DrawerTest.kt
M ui/ui-material/src/androidTest/java/androidx/ui/material/ProgressIndicatorTest.kt
M ui/ui-material/src/androidTest/java/androidx/ui/material/RadioGroupUiTest.kt
M ui/ui-material/src/main/java/androidx/ui/material/Color.kt
M ui/ui-material/src/main/java/androidx/ui/material/Scaffold.kt
M ui/ui-test/src/androidTest/java/androidx/ui/test/AndroidComposeTestCaseRunnerTest.kt
M ui/ui-test/src/androidTest/java/androidx/ui/test/MultipleComposeRootsTest.kt
M ui/ui-text/src/main/java/androidx/ui/text/CoreText.kt
M ui/ui-text/src/main/java/androidx/ui/text/CoreTextField.kt
co...@gmail.com <co...@gmail.com> #8
What did you think of my solution that uses the init() function passed to the rememberSaveable()? This prevents needing to save anything about the policy and would support any mutation policy.
I'm not sure if the docs say anywhere that the init() function will only be called the first time and using it to restore the state would be incorrect.
I've been using my workaround fairly extensively and it has been great.
an...@google.com <an...@google.com> #9
Yes, this also works. Feel free to use it until we provide the correct fix in the library
ap...@google.com <ap...@google.com> #10
Branch: androidx-main
commit dd000ea5e6eb3a8f07b869595ca62aa2fb781d12
Author: Andrey Kulikov <andreykulikov@google.com>
Date: Mon Feb 22 19:42:13 2021
Fix for broken rememberSaveable { mutableStateOf(0) } with navigation-compose
Previously we were converting MutableState into a parcelable structure only for a direct child of the saving list. It was incorrect as SaveableStateHolder used in navigation is wrapping the state of the screen as a value into a Map. Now we will recursively iterate through List/Maps and covert all inner MutableState objects.
Fixes: 180042685
Fixes: 180701630
Test: new tests in ActivityRecreationTest
Relnote: Fix for broken rememberSaveable { mutableStateOf(0) } when used inside a destination of navigation-compose.
Change-Id: I1312b5b210dde32250945d164a2f3a1b574cb0a8
M compose/runtime/runtime-saveable/src/androidAndroidTest/AndroidManifest.xml
M compose/runtime/runtime-saveable/src/androidAndroidTest/kotlin/androidx/compose/runtime/saveable/ActivityRecreationTest.kt
M compose/runtime/runtime-saveable/src/commonMain/kotlin/androidx/compose/runtime/saveable/RememberSaveable.kt
M compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/DisposableSaveableStateRegistry.android.kt
an...@google.com <an...@google.com> #11
ba...@gmail.com <ba...@gmail.com> #12
It looks like this bug has been re-introduced in 1.0
an...@google.com <an...@google.com> #13
el...@kroger.com <el...@kroger.com> #14
Hi I am seeing this same issue using navigation-compose version 2.5.2, we tried the suggestions from this thread from 2021 but they did not work. The stack trace looks like this:
java.lang.RuntimeException: Parcelable encountered IOException writing serializable object (name = [Landroidx.compose.runtime.MutableState;)
at android.os.Parcel.writeSerializable(Parcel.java:1833)
at android.os.Parcel.writeValue(Parcel.java:1780)
at android.os.Parcel.writeList(Parcel.java:1045)
at android.os.Parcel.writeValue(Parcel.java:1729)
at android.os.Parcel.writeMapInternal(Parcel.java:896)
at android.os.Parcel.writeMap(Parcel.java:878)
at android.os.Parcel.writeValue(Parcel.java:1694)
at android.os.Parcel.writeMapInternal(Parcel.java:896)
at android.os.Parcel.writeMap(Parcel.java:878)
at android.os.Parcel.writeValue(Parcel.java:1694)
at android.os.Parcel.writeList(Parcel.java:1045)
at android.os.Parcel.writeValue(Parcel.java:1729)
at android.os.Parcel.writeArrayMapInternal(Parcel.java:928)
at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1584)
at android.os.Bundle.writeToParcel(Bundle.java:1253)
at android.os.Parcel.writeBundle(Parcel.java:997)
at android.os.Parcel.writeValue(Parcel.java:1698)
at android.os.Parcel.writeArrayMapInternal(Parcel.java:928)
...
at android.app.IActivityTaskManager$Stub$Proxy.activityStopped(IActivityTaskManager.java:4505)
at android.app.servertransaction.PendingTransactionActions$StopInfo.run(PendingTransactionActions.java:145)
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)
Caused by: java.io.NotSerializableException: androidx.compose.runtime.ParcelableSnapshotMutableState
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1240)
at java.io.ObjectOutputStream.writeArray(ObjectOutputStream.java:1434)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1230)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:354)
at android.os.Parcel.writeSerializable(Parcel.java:1828)
at android.os.Parcel.writeValue(Parcel.java:1780)
at android.os.Parcel.writeList(Parcel.java:1045)
at android.os.Parcel.writeValue(Parcel.java:1729)
at android.os.Parcel.writeMapInternal(Parcel.java:896)
at android.os.Parcel.writeMap(Parcel.java:878)
...
an...@google.com <an...@google.com> #15
If you can file a new bug with the reproducible code sample we will be able to take a look
ga...@gmail.com <ga...@gmail.com> #16
E FATAL EXCEPTION: main
Process: com.example.animeapp, PID: 12273
java.lang.RuntimeException: Parcel: unable to marshal value Color(0.83137256, 0.75686276, 0.8156863, 1.0, sRGB IEC61966-2.1)
at android.os.Parcel.writeValue(Parcel.java:1885)
at android.os.Parcel.writeList(Parcel.java:1092)
at android.os.Parcel.writeValue(Parcel.java:1832)
at android.os.Parcel.writeList(Parcel.java:1092)
at android.os.Parcel.writeValue(Parcel.java:1832)
at android.os.Parcel.writeMapInternal(Parcel.java:943)
at android.os.Parcel.writeMap(Parcel.java:925)
at android.os.Parcel.writeValue(Parcel.java:1797)
at android.os.Parcel.writeMapInternal(Parcel.java:943)
at android.os.Parcel.writeMap(Parcel.java:925)
at android.os.Parcel.writeValue(Parcel.java:1797)
at android.os.Parcel.writeList(Parcel.java:1092)
at android.os.Parcel.writeValue(Parcel.java:1832)
at android.os.Parcel.writeArrayMapInternal(Parcel.java:975)
at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1620)
at android.os.Bundle.writeToParcel(Bundle.java:1303)
at android.os.Parcel.writeBundle(Parcel.java:1044)
at android.os.Parcel.writeValue(Parcel.java:1801)
at android.os.Parcel.writeArrayMapInternal(Parcel.java:975)
at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1620)
at android.os.Bundle.writeToParcel(Bundle.java:1303)
at android.os.Parcel.writeBundle(Parcel.java:1044)
at android.os.Parcel.writeValue(Parcel.java:1801)
at android.os.Parcel.writeArrayMapInternal(Parcel.java:975)
at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1620)
at android.os.Bundle.writeToParcel(Bundle.java:1303)
at android.app.IActivityTaskManager$Stub$Proxy.activityStopped(IActivityTaskManager.java:4389)
at android.app.servertransaction.PendingTransactionActions$StopInfo.run(PendingTransactionActions.java:145)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7656)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
an...@google.com <an...@google.com> #17
ma...@marcardar.com <ma...@marcardar.com> #18
Related to this issue, it would be useful to give more details in the log message, because when, for example, the object is a Set
we have no idea what is the underlying type (the type of the items stored in the Set
) and so have no idea where to look in the code:
Fatal Exception: java.lang.RuntimeException: Parcelable encountered IOException writing serializable object (name = java.util.LinkedHashSet)
at android.os.Parcel.writeSerializable(Parcel.java:2178)
at android.os.Parcel.writeValue(Parcel.java:1944)
at androidx.compose.runtime.ParcelableSnapshotMutableState.writeToParcel(ParcelableSnapshotMutableState.kt:30)
at android.os.Parcel.writeParcelable(Parcel.java:1965)
at android.os.Parcel.writeValue(Parcel.java:1871)
at android.os.Parcel.writeList(Parcel.java:1153)
at android.os.Parcel.writeValue(Parcel.java:1893)
at android.os.Parcel.writeMapInternal(Parcel.java:1004)
at android.os.Parcel.writeMap(Parcel.java:986)
at android.os.Parcel.writeValue(Parcel.java:1858)
at android.os.Parcel.writeMapInternal(Parcel.java:1004)
at android.os.Parcel.writeMap(Parcel.java:986)
at android.os.Parcel.writeValue(Parcel.java:1858)
at android.os.Parcel.writeList(Parcel.java:1153)
at android.os.Parcel.writeValue(Parcel.java:1893)
at android.os.Parcel.writeMapInternal(Parcel.java:1004)
at android.os.Parcel.writeMap(Parcel.java:986)
at android.os.Parcel.writeValue(Parcel.java:1858)
at android.os.Parcel.writeList(Parcel.java:1153)
at android.os.Parcel.writeValue(Parcel.java:1893)
at android.os.Parcel.writeArrayMapInternal(Parcel.java:1036)
at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1620)
at android.os.Bundle.writeToParcel(Bundle.java:1304)
at android.os.Parcel.writeBundle(Parcel.java:1105)
at android.os.Parcel.writeValue(Parcel.java:1862)
at android.os.Parcel.writeArrayMapInternal(Parcel.java:1036)
at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1620)
at android.os.Bundle.writeToParcel(Bundle.java:1304)
at android.os.Parcel.writeBundle(Parcel.java:1105)
at android.os.Parcel.writeValue(Parcel.java:1862)
at android.os.Parcel.writeArrayMapInternal(Parcel.java:1036)
at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1620)
at android.os.Bundle.writeToParcel(Bundle.java:1304)
at android.os.Parcel.writeBundle(Parcel.java:1105)
at android.os.Parcel.writeValue(Parcel.java:1862)
at android.os.Parcel.writeArrayMapInternal(Parcel.java:1036)
at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1620)
at android.os.Bundle.writeToParcel(Bundle.java:1304)
at android.os.Parcel.writeBundle(Parcel.java:1105)
at android.os.Parcel.writeValue(Parcel.java:1862)
at android.os.Parcel.writeArrayMapInternal(Parcel.java:1036)
at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1620)
at android.os.Bundle.writeToParcel(Bundle.java:1304)
at android.os.Parcel.writeBundle(Parcel.java:1105)
at android.os.Parcel.writeValue(Parcel.java:1862)
at android.os.Parcel.writeArrayMapInternal(Parcel.java:1036)
at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1620)
at android.os.Bundle.writeToParcel(Bundle.java:1304)
at android.os.Parcel.writeBundle(Parcel.java:1105)
at android.os.Parcel.writeValue(Parcel.java:1862)
at android.os.Parcel.writeArrayMapInternal(Parcel.java:1036)
at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1620)
at android.os.Bundle.writeToParcel(Bundle.java:1304)
at android.os.Parcel.writeBundle(Parcel.java:1105)
at android.os.Parcel.writeValue(Parcel.java:1862)
at android.os.Parcel.writeArrayMapInternal(Parcel.java:1036)
at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1620)
at android.os.Bundle.writeToParcel(Bundle.java:1304)
at android.os.Parcel.writeBundle(Parcel.java:1105)
at android.os.Parcel.writeValue(Parcel.java:1862)
at android.os.BaseBundle.dumpStats(BaseBundle.java:1690)
at android.os.BaseBundle.dumpStats(BaseBundle.java:1727)
at android.app.servertransaction.PendingTransactionActions$StopInfo.run(PendingTransactionActions.java:150)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loopOnce(Looper.java:226)
at android.os.Looper.loop(Looper.java:313)
at android.app.ActivityThread.main(ActivityThread.java:8751)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:571)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1135)
il...@google.com <il...@google.com> #19
Re
Description
Component used: Compose Version used: 1.0.0-alpha12 Devices/Android versions reproduced on: Pixel 4 Android 11
The recommended code to replace
savedInstanceState{ 0 }
isrememberSaveable { mutableStateOf(0) }
However, I've found this causes a crash whenever the app is minimized.
The exception thrown is
java.lang.RuntimeException: Parcel: unable to marshal value androidx.compose.runtime.SnapshotMutableStateImpl@5a847c2
After digging into the code I believe this is caused by the special version of
rememberSaveable
forMutableState
objects doesn't have a default saver parameter so the normal version is used which doesn't know how to save aMutableState
.I think changing the
rememberSaveable
definition fromto
would fix this.