One Off the Slack: When Do Orientation Changes Take Effect?

Rick Regan asked:

I’m tracing through composition during device rotation and I see behavior I wasn’t expecting. I ran this composable in a Pixel 4A on the emulator:

@Composable
fun Dimensions() {
  BoxWithConstraints {
    val orientation = if (LocalConfiguration.current.orientation == Configuration.ORIENTATION_PORTRAIT) "P" else "L"
    println("$orientation: w = $maxWidth, h = $maxHeight")
  }
}

I started in portrait orientation and then rotated the device left 8 times (with android:screenOrientation="fullUser" to allow it to render upside down). There were three unexpected things about the output:

(1) Duplicate messages for a given rotation

(2) Change in orientation is not in sync with the change in dimensions

(3) Incorrect intermediate dimensions

(1) I suppose is just Compose working as advertised (duplicate recompositions), but (2) and (3) I don’t understand.

Google’s Adam Powell explained:

Composition happens before layout does, and layout is when constraints are determined. The system doesn’t know at composition time that the layout constraints are going to change, (the composition you’re about to perform can determine that,) and the BoxWithConstraints subcomposition recomposes in the normal recompose step as a result of LocalConfiguration changing. Then layout happens and the contents are measured with the new constraints, so BoxWithConstraints recomposes again, now with the new constraints.

Rick was seeing rotation artifacts:

When I hit a breakpoint in my app during rotation to landscape I can see the top part of my portrait layout in the left half of the screen (the right half is black). At full-speed on a device I can’t see that, though now I wonder if this is why rotation in my app never looked as smooth as rotation in the Gmail or Drive apps, for example.

Adam clarified:

the additional recomposition I described happens in the same frame before drawing

Rick still was concerned:

Ok thanks. Not knowing the internals too well I didn’t know you could have multiple compositions per frame. But this is consistent with the testing I was just doing – trying to capture the rotation on a device by recording it at 60fps on another. I never did see that “half frame” that I see on the emulator. I guess my app’s lack of smoothness is just in my head 🙂

Adam pointed out a possible reason for the behavior:

well, it could also be that the recompositions involved are just running a lot of code to perform that recomposition. 🙂 https://ui.perfetto.dev and placing your own trace points can be a good way to see where some of the time is going in a release build


Read the original thread in the kotlinlang Slack workspace. Not a member? Join that Slack workspace here!