2

My apps performance is pretty good overall, the only part where the FPS drops is when swiping through the HorizontalPager by accompanist.

Each Page has a simple LazyVerticalGrid with 3 fixed columns but the performance is way worse compared to XML.

Here is how the LazyVerticalGrid looks like:

enter image description here

For testing purposes I developed the same setup in XML to compare it using Perfetto and the Android Studio Compiler. The Jetpack Compose version has a lot more Janky frames compared to XML.

Here is the result of compose in Perfetto:

enter image description here

And here XML:

enter image description here

Also the Profiler is showing a bunch of janky frames each time I swipe the HorizontalPager:

enter image description here

I have basic knowledge of Jetpack Compose and also read through the Android Developer Compose Performance guide and I am pretty sure that I am not doing anything stupid which causes a ton of recompositions.

Here is the code I used in my testproject:

Scrollable TabRow + HorizontalPager:

val tabList = listOf("Following", "Trending", "New", "Top", "Most Shared", "Most Saved", "All")
val pagerState: PagerState = rememberPagerState(initialPage = 1)
val coroutineScope = rememberCoroutineScope()

Column(
    modifier = Modifier
        .fillMaxSize()
        .background(MaterialTheme.colors.background)
) {
    ScrollableTabRow(
        modifier = Modifier.fillMaxWidth(),
        backgroundColor = MaterialTheme.colors.surface,
        contentColor = Color.White,
        edgePadding = 8.dp, 
        selectedTabIndex = pagerState.currentPage, 
        indicator = { tabPositions ->
            TabRowDefaults.Indicator(
                Modifier.pagerTabIndicatorOffset(pagerState, tabPositions),
                color = MaterialTheme.colors.primary
            )
        }
    ) {
        // Add tabs for all of our pages
        tabList.forEachIndexed { index, title ->
            Tab(
                text = { Text(title) },
                selected = pagerState.currentPage == index,
                onClick = {
                    coroutineScope.launch {
                        pagerState.animateScrollToPage(index)
                    }
                },
            )
        }
    }

    HorizontalPager(
        state = pagerState,
        count = tabList.size
    ) { page: Int ->
        when (page) {
            0 -> MyList()
            1 -> MyList()
            2 -> MyList()
            3 -> MyList()
            4 -> MyList()
            5 -> MyList()
            6 -> MyList()
        }
    }

MyList:

@Composable
fun MyList(){
    LazyVerticalGrid(
        modifier = Modifier.fillMaxSize(),
        columns = GridCells.Fixed(3),
        content = {
            items(100) { item ->

                Button(onClick = { /*TODO*/ }) {
                    Text(text = "Hello")
                }
            }
        })


}

I have R8 enabled and running in release mode on a Samsung Galaxy s10+. Overall the performance with the setup from above is not making any huge visible FPS drops but its still noticably not as smooth as XML, the problem is that I need a little bit more inside the LazyVerticalGrid items than just a simple button with the words "Hello" . For my setup I need in each item:

  • 1x 512x512 webp image
  • 2x TextView
  • 2x Icons
  • 1x Card with elevation

The more I add to the items the more visible the lags get. This is roughly how the items will look like:

enter image description here

Running this inside my Jetpack Compose Horizontal Pager is causing massive lags (10 fps aprox) and is causing a very poor user experience.

Someone from the Jetpack Compose Slack channel suggested defining baseline profiles, as far as I understood correctly those will only boost the startup performance and the first time a library gets used. So unfortunately this will not fix my HorizontalPager performance.

Are there any ideas how to boost the HorizontalPager performance besides switching back to XML?

Currenlty using Compose Version 1.3.0-beta01 and Kotlin 1.7.10. Would be happy if someone could tell me if the code above also causes janky frames on their device.

UPDATE

I created up a Baseline Profile for the Horizontal Pager swipe gesture and tested it with Macrobenchmark. The results look promising:

enter image description here

But unfortunately there is no visible change when sideloading the baseline profile, its still janky.

Here is the Baseline Profile:

@RunWith(AndroidJUnit4::class)
class BaselineProfileGenerator {

    @OptIn(ExperimentalBaselineProfilesApi::class)
    @get:Rule
    val rule = BaselineProfileRule()

    @OptIn(ExperimentalBaselineProfilesApi::class)
    @Test
    fun generate() {
        rule.collectBaselineProfile("com.example.composespeedtest") {


            startActivityAndWait()
            device.wait(Until.hasObject(By.res("horizontal_pager")), 10_000)
            val snackList = device.findObject(By.res("horizontal_pager"))
            snackList.setGestureMargin(device.displayWidth / 5)
            snackList.scroll(Direction.RIGHT, 1f)
            device.waitForIdle()
 
        }
    }
}
mlykotom
  • 4,850
  • 1
  • 22
  • 27
HavanaSun
  • 446
  • 3
  • 12
  • 39
  • Have you only tested it while compiled for debugging? Try compiling a production version and testing that. There is a dramatic performance difference between the two with Compose. – Khantahr Sep 01 '22 at 19:08
  • @Khantahr yes I have R8 enabled and running in release mode – HavanaSun Sep 01 '22 at 19:19
  • @HavanaSun did you by any chance fix this? I am getting the same poor performance using a HorizontalPager – rosu alin Jul 10 '23 at 14:09

0 Answers0