simple-stack-compose-integration icon indicating copy to clipboard operation
simple-stack-compose-integration copied to clipboard

Does Simple-Stack supports hot-reload?

Open lcszc opened this issue 1 year ago • 3 comments

I've been using Simple-Stack and Compose Integration quite a lot lately. While both work normally in production without any issues, I'd like to know if there's something we can do to fix hot-reload.

I know that hot-reload is still a dream for Android devs. Still, on Android Studio we have live edit for Compose that surprisingly works fine in the latest version of Android Studio but I've been facing crashes whenever I edit some Composable:

java.lang.IllegalStateException: State change completion cannot be called multiple times!
at com.zhuinden.simplestack.NavigationCore$1.stateChangeComplete(NavigationCore.java:714)
at com.zhuinden.simplestackcomposeintegration.core.ComposeStateChanger$DetermineDisplayedScreens$1.invokeSuspend(ComposeIntegrationCore.kt:336

lcszc avatar Feb 21 '25 13:02 lcszc

I forgot to open Github notifications for a while, the question is valid, but I don't know what lifecycle calls Android Studio is trying to do at first glance for live-edit to work.

I would imagine this to happen only if they re-execute onCreate() on an Activity that already exists, which is.. unprecedented. 🤔

Zhuinden avatar Mar 02 '25 20:03 Zhuinden

Yeah I haven't investigated this yet but I'll keep you posted if I find something

lcszc avatar Mar 03 '25 18:03 lcszc

I feel like the only way to figure this one out is System.out.println()ing in various functions.

Although I would NOT be surprised if this is unique to something in the compose integration for how completionCallback.stateChangeComplete() is called.

Zhuinden avatar Mar 03 '25 19:03 Zhuinden

@lcszc have you found anything yet? i have not tried using compose hot-reload honestly if not for the github notification, I just completely forgot about this.

Zhuinden avatar Apr 01 '25 00:04 Zhuinden

@Zhuinden, I'm sorry for my delay in checking this out! I just now figured out what's happening.

Gladly the Activity is not recreated. Instead setContent is recomposed, forcing your app to be redrawn entirely.

This is the setup that was crashing the app on live edits:

// Your Activity....
private lateinit var backstack: Backstack
private lateinit var composeStateChanger: ComposeStateChanger

private val backPressedCallback = object : OnBackPressedCallback(false) {
    override fun handleOnBackPressed() {
        backstack.goBack()
    }
}

// Then this in onCreate

composeStateChanger = ComposeStateChanger()

// and this under setContent {}
BackstackProvider(backstack) {
    Theme {
         Box(Modifier.fillMaxSize()) {
              composeStateChanger.RenderScreen()
        }
    }
}

This is the known setup for using Simple-Stack, which I always use so I thought it was fine. I guess this is what you recommend in the docs as well.

Anyway, to make live edits to work without issues, I've used the Compose-friendly version of Simple-Stack that allows me to customize it the way I need:

// This is your setContent {}
val composeStateChanger = remember {
    ComposeStateChanger()
}
val asyncStateChanger = remember(composeStateChanger) {
    AsyncStateChanger(composeStateChanger)
}

val backstack = rememberBackstack(asyncStateChanger) {
    createBackstack(
        scopedServices = DefaultServiceProvider(),
        globalServices = app.globalServices,
        initialKeys = History.of(SplashKey())
    )
}

BackstackProvider(backstack) {
    Theme {
        Box(Modifier.fillMaxSize()) {
            composeStateChanger.RenderScreen()
        }
    }
}

This is the result:

https://github.com/user-attachments/assets/e319c30c-4710-4b1e-b47e-b5cc70584705

lcszc avatar May 13 '25 13:05 lcszc

I'd betComposeNavigator works fine but I haven't tried it

lcszc avatar May 13 '25 13:05 lcszc