android / nowinandroid

A fully functional Android app built entirely with Kotlin and Jetpack Compose
Apache License 2.0
16.87k stars 3.07k forks source link

[Bug]: SearchRoute recomposing twice despite no state change ? #1141

Open mahmed1987 opened 10 months ago

mahmed1987 commented 10 months ago

Is there an existing issue for this?

Is there a StackOverflow question about this issue?

What happened?

I am not sure if this is a bug or not but as per basic Compose tenets , a composable will NOT recompose if its state remains unchanged from the first time it was composed .

I have attached a breakpoint and confirmed that when SearchRoute recomposes , none of its arguments have changed.

I am aware that this composable's callsite does gets recomposed multiple times , but I am unsure as to why does this cause its child composable (SearchRoute) to recompose .

This is strange specially because we are not passing unstable lambdas in SearchRoute , rather are relying on method references (which correctly remain the same upon recomposition).

Take a look at both these screenshots , and observe all references are similar. One screenshot was taken on first time around , and the second is of the recomposition

image image

Relevant logcat output

No response

Code of Conduct

AntonButov commented 9 months ago

Hi, I tried to research this case. This is what I found out. You are right fun SearchRoute() should be skipable.

restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun SearchRoute(
  stable modifier: Modifier? = @static Companion
  stable onBackClick: Function0<Unit>
  stable onInterestsClick: Function0<Unit>
  stable onTopicClick: Function1<String, Unit>
  bookmarksViewModel: BookmarksViewModel? = @dynamic hiltViewModel(null, $composer, 0, 0b0001)
  interestsViewModel: InterestsViewModel? = @dynamic hiltViewModel(null, $composer, 0, 0b0001)
  unstable searchViewModel: SearchViewModel? = @dynamic hiltViewModel(null, $composer, 0, 0b0001)
  forYouViewModel: ForYouViewModel? = @dynamic hiltViewModel(null, $composer, 0, 0b0001)

But Layout Insperector shows that on every search SearchRoute run twice:

2024-01-10_14-53-55

I wrapped the function like that:

2024-01-10_15-12-20

The SearchRoute runs ones

2024-01-10_15-07-02

2024-01-10_15-17-28

I fink SearchRouteInternal runs several times because we use

2024-01-10_15-19-23

in it.

mahmed1987 commented 9 months ago

hey @AntonButov .

This is something to do with the passed lambdas , which are hooked onto method references , yet their usage is causing unnecessary recompositions .

I made a small project to demonstrate this here .

For some reason , despite using method references , instead of lambdas , we are getting state changes

You will find that despite absolutely no state change , the the target composable marked skippable , the Composable always recomposes .