Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Navigation is a pain with Navigation Compose API #149

Open
ksharma-xyz opened this issue Oct 6, 2024 · 5 comments
Open

Navigation is a pain with Navigation Compose API #149

ksharma-xyz opened this issue Oct 6, 2024 · 5 comments
Assignees
Labels
improvement To improve the current code quality

Comments

@ksharma-xyz
Copy link
Owner

ksharma-xyz commented Oct 6, 2024

I would really like to pass some data around different screens such as StopItem, but need to implement Parcelable for all data types used.

data class StopItem(
    val stopName: String,
    val transportModes: ImmutableSet<TransportModeType> = persistentSetOf(),
    val stopId: String,
): Serializable

I am unable to do this:

val fromStopItem = backStackEntry.savedStateHandle.get<StopItem>(SearchStopFieldType.FROM.key)
val toStopItem = backStackEntry.savedStateHandle.get<StopItem>(SearchStopFieldType.TO.key)
FATAL EXCEPTION: main (Ask Gemini)
                        Process: xyz.ksharma.krail.debug, PID: 11280
                        android.os.BadParcelableException: Parcelable encountered IOException writing serializable object (name = xyz.ksharma.krail.trip_planner.ui.state.searchstop.model.StopItem)
                        	at android.os.Parcel.writeSerializable(Parcel.java:2907)
                        	at android.os.Parcel.writeValue(Parcel.java:2673)
                        	at android.os.Parcel.writeValue(Parcel.java:2472)
                        	at android.os.Parcel.writeList(Parcel.java:1453)
                        	at android.os.Parcel.writeValue(Parcel.java:2616)
                        	at android.os.Parcel.writeValue(Parcel.java:2472)
                        	at android.os.Parcel.writeArrayMapInternal(Parcel.java:1336)
                        	at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1844)
                        	at android.os.Bundle.writeToParcel(Bundle.java:1390)
                        	at android.os.Parcel.writeBundle(Parcel.java:1405)
                        	at android.os.Parcel.writeValue(Parcel.java:2589)
                        	at android.os.Parcel.writeValue(Parcel.java:2479)
                        	at android.os.Parcel.writeArrayMapInternal(Parcel.java:1336)
                        	at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1844)
                        	at android.os.Bundle.writeToParcel(Bundle.java:1390)
                        	at android.os.Parcel.writeBundle(Parcel.java:1405)
                        	at android.os.Parcel.writeValue(Parcel.java:2589)
                        	at android.os.Parcel.writeValue(Parcel.java:2479)
                        	at android.os.Parcel.writeArrayMapInternal(Parcel.java:1336)
                        	at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1844)
                        	at android.os.Bundle.writeToParcel(Bundle.java:1390)
                        	at android.os.Parcel.writeBundle(Parcel.java:1405)
                        	at androidx.navigation.NavBackStackEntryState.writeToParcel(NavBackStackEntryState.kt:73)
                        	at android.os.Parcel.writeParcelable(Parcel.java:2694)
                        	at android.os.Parcel.writeParcelableArray(Parcel.java:4507)
                        	at android.os.Parcel.writeValue(Parcel.java:2637)
                        	at android.os.Parcel.writeValue(Parcel.java:2472)
                        	at android.os.Parcel.writeArrayMapInternal(Parcel.java:1336)
                        	at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1844)
                        	at android.os.Bundle.writeToParcel(Bundle.java:1390)
                        	at android.os.Parcel.writeBundle(Parcel.java:1405)
                        	at android.os.Parcel.writeValue(Parcel.java:2589)
                        	at android.os.Parcel.writeValue(Parcel.java:2479)
                        	at android.os.Parcel.writeList(Parcel.java:1453)
                        	at android.os.Parcel.writeValue(Parcel.java:2616)
                        	at android.os.Parcel.writeValue(Parcel.java:2472)
                        	at android.os.Parcel.writeArrayMapInternal(Parcel.java:1336)
                        	at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1844)
                        	at android.os.Bundle.writeToParcel(Bundle.java:1390)
                        	at android.os.Parcel.writeBundle(Parcel.java:1405)
                        	at android.os.Parcel.writeValue(Parcel.java:2589)
                        	at android.os.Parcel.writeValue(Parcel.java:2479)
                        	at android.os.Parcel.writeArrayMapInternal(Parcel.java:1336)
                        	at android.os.BaseBundle.writeToParcelInner(BaseBundle.java:1844)
                        	at android.os.Bundle.writeToParcel(Bundle.java:1390)
                        	at android.os.Parcel.writeBundle(Parcel.java:1405)
                        	at android.os.Parcel.writeValue(Parcel.java:2589)
                        	at android.os.Parcel.writeValue(Parcel.java:2479)
                        	at android.os.BaseBundle.dumpStats(BaseBundle.java:1918)
                        	at android.os.BaseBundle.dumpStats(BaseBundle.java:1955)
                        	at android.app.servertransaction.PendingTransactionActions$StopInfo.collectBundleStates(PendingTransactionActions.java:123)
                        	at android.app.servertransaction.PendingTransactionActions$StopInfo.run(PendingTransactionActions.java:139)
                        	at android.os.Handler.handleCallback(Handler.java:959)
                        	at android.os.Handler.dispatchMessage(Handler.java:100)
                        	at android.os.Looper.loopOnce(Looper.java:232)
                        	at android.os.Looper.loop(Looper.java:317)
                        	at android.app.ActivityThread.main(ActivityThread.java:8592)
                        	at java.lang.reflect.Method.invoke(Native Method)
                        	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:580)
                        	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:878)
                        Caused by: java.io.NotSerializableException: kotlinx.collections.immutable.implementations.persistentOrderedSet.PersistentOrderedSet
                        	at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1240)
                        	at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1620) (Ask Gemini)
                        	at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1581)
                        	at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1490)
                        	at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1234)
                        	at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:354)
                        	at android.os.Parcel.writeSerializable(Parcel.java:2902)
@ksharma-xyz ksharma-xyz added the improvement To improve the current code quality label Oct 6, 2024
@ksharma-xyz
Copy link
Owner Author

Probably using https://github.com/slackhq/circuit is a good idea, but that would mean using MVP? Need to explore

@ksharma-xyz
Copy link
Owner Author

ksharma-xyz commented Oct 6, 2024

As a temporary workaround will serialize the concrete data types into JSON format and then deserialize them back into their original concrete types.

val fromStopItem: StopItem? = backStackEntry.savedStateHandle.get<String>(SearchStopFieldType.FROM.key)
        ?.let { fromJsonString(it) }
val toStopItem: StopItem? = backStackEntry.savedStateHandle.get<String>(SearchStopFieldType.TO.key)
        ?.let { fromJsonString(it) }

This creates a core problem, that now values are nullable and it needs to be handled. We should be able to pass data around, and ensure it will be non null.

#150

@ksharma-xyz
Copy link
Owner Author

ksharma-xyz commented Oct 6, 2024

Two major issues with using Navigation Compose API:

  • Cannot pass around data
  • Need lot of boiler plate logic to pass around data
  • Values passed around are nullable

@ksharma-xyz ksharma-xyz self-assigned this Oct 6, 2024
@ksharma-xyz
Copy link
Owner Author

ksharma-xyz commented Oct 12, 2024

Another problem:

I had to create two different variables for saving one thing. In this PR #168 ,

// This holds Arguments value
val fromArg = backStackEntry.savedStateHandle.get<String>(SearchStopFieldType.FROM.key)?.let { fromJsonString(it) }
val toArg = backStackEntry.savedStateHandle.get<String>(SearchStopFieldType.TO.key)?.let { fromJsonString(it) }

// This holds state value for Screen
var fromStopItem: StopItem? by rememberSaveable { mutableStateOf(fromArg) }
var toStopItem: StopItem? by rememberSaveable { mutableStateOf(toArg) }

The first variable fromArg represents the argument, and the second variable fromStopItem represents the state.

When reverse button is clicked, then the data has to be changed twice because of two variables requirement.

Timber.d("onReverseButtonClick:")
val bufferStop = fromStopItem
backStackEntry.savedStateHandle[SearchStopFieldType.FROM.key] = toStopItem?.toJsonString()
backStackEntry.savedStateHandle[SearchStopFieldType.TO.key] = bufferStop?.toJsonString()

fromStopItem = toStopItem
toStopItem = bufferStop
  • It is also harder to do this reverse stop feature logic if I need to update the state inside viewmodel as well.
  • Another problem is that by using rememberSaveable, the app will crash when trying to save state in background as StopItem is not parcelable. Here's a PR to fix this Fix StopItem parcelable crash #174

@ksharma-xyz
Copy link
Owner Author

Exploring Circuit here - https://github.com/ksharma-xyz/Circuit-Demo

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
improvement To improve the current code quality
Projects
Status: TODO
Development

No branches or pull requests

1 participant