I'm tracing through composition during device rota...
# compose
r
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:
Copy code
@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. (See 🧵for details.)
This is the output of the program, with one example of each unexpected message highlighted:
Copy code
startup
P: w = 392.72726.dp, h = 785.4545.dp
P: w = 392.72726.dp, h = 785.4545.dp <= (1) duplicate message

rotate L
L: w = 392.72726.dp, h = 785.4545.dp <= (2) new orientation, old dimensions
L: w = 801.4545.dp, h = 348.72726.dp
L: w = 801.4545.dp, h = 348.72726.dp
rotate L
P: w = 801.4545.dp, h = 348.72726.dp
P: w = 392.72726.dp, h = 736.0.dp
P: w = 392.72726.dp, h = 736.0.dp
rotate L
L: w = 392.72726.dp, h = 736.0.dp
L: w = 801.4545.dp, h = 277.81818.dp <= (3) incorrect intermediate height
L: w = 801.4545.dp, h = 348.72726.dp
L: w = 801.4545.dp, h = 348.72726.dp
rotate L
P: w = 801.4545.dp, h = 348.72726.dp
P: w = 392.72726.dp, h = 785.4545.dp
P: w = 392.72726.dp, h = 785.4545.dp

rotate L
L: w = 392.72726.dp, h = 785.4545.dp
L: w = 801.4545.dp, h = 348.72726.dp
L: w = 801.4545.dp, h = 348.72726.dp
rotate L
P: w = 801.4545.dp, h = 348.72726.dp
P: w = 392.72726.dp, h = 736.0.dp
P: w = 392.72726.dp, h = 736.0.dp
rotate L
L: w = 392.72726.dp, h = 736.0.dp
L: w = 801.4545.dp, h = 348.72726.dp
L: w = 801.4545.dp, h = 348.72726.dp
rotate L
P: w = 801.4545.dp, h = 348.72726.dp
P: w = 392.72726.dp, h = 785.4545.dp
P: w = 392.72726.dp, h = 785.4545.dp
a
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.
👍 1
r
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.
a
the additional recomposition I described happens in the same frame before drawing
r
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 🙂
a
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
r
Thanks for the link. Performance testing is on my todo list.