Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Optimizing Compose App – Tools , Tips and Perfo...

ThawZinToe
November 06, 2023

Optimizing Compose App – Tools , Tips and Performance Guidelines

This is the DevFest Slide for How to Optimize for your Android industry project that is developed with compose. I will explain basic Baseline Profile workflow, tips and tricks, and how to set up and report your Compose Compiler and Metrics reports that can write scripts for your Continuous Integration Server.

ThawZinToe

November 06, 2023
Tweet

More Decks by ThawZinToe

Other Decks in Technology

Transcript

  1. Overview 01 02 Use R8 Take advantages using Baseline Profile

    Speed up start up and hot paths Enable optimizing, shrinking & security with R8 compiler 03 Understanding Compose Phases Some phase can make your app will slow down 04 05 Compose Best Practices Diagnose & Fix Stability issues What & how to fix it Common mistakes for compose recomposition
  2. AppType Size App Launch Mode Non Minified 11.8 MB 596.6

    ms Release Minified 6.5 MB 367.9 ms Release Minified + Shrinked 6.4 MB 361.3 ms Release Comparison
  3. What is R8? Remove unused classes, functions and fields Remove

    unused resources Optimize the code itself Obfuscates your code Tool to optimize your app for release
  4. android { buildTypes { getByName("release") { isMinifyEnabled = true isShrinkResources

    = true proguardFiles( getDefaultProguardFile("proguard-android-optimize.txt"), "Proguard-rules.pro" ) } } } R8 optimization
  5. @Composable fun ContactList( contacts: List<Contact>, comparator: Comparator<Contact>, modifier: Modifier =

    Modifier ) { LazyColumn(modifier) { // DON’T DO THIS items(contacts.sortedWith(comparator)) { contact -> // ... } } }
  6. @Composable fun ContactList( contacts: List<Contact>, comparator: Comparator<Contact>, modifier: Modifier =

    Modifier ) { val sortedContacts = remember(contacts, comparator) { contacts.sortedWith(comparator) } LazyColumn(modifier) { items(sortedContacts) { // ... } } }
  7. fun LazyColumn{ items(contacts, keys = { item.id } ) {

    contact -> … } } Use a key parameter to provide a unique key for each item
  8. val listState = rememberLazyListState() fun LazyColumn(state = listState){ // …

    } val showButton = listState.firstVisibleItemIndex > 0 AnimatedVisibility(visible = showButton) { ScrollToTopButton() } Top
  9. val listState = rememberLazyListState() fun LazyColumn(state = listState){ // …

    } val showButton = remember { derivedStateOf { listState.firstVisibleItemIndex > 0 } } AnimatedVisibility(visible = showButton) { ScrollToTopButton() }
  10. val color by animateColorBetween(Color.Green, Color.Blue) Box( Modifier .fillMaxSize() .drawBehind {

    drawRect(color) } ) function instance called in draw, no composition or layout
  11. @Composable fun ContactCard(contact: Contact) { MyCard { Text(“Name: ${contact.name}”) }

    } Only this call is executed when contact’s .name changes
  12. Setup to Compose Compiler Reports in build.gradle for root level

    project tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach { kotlinOptions { if (project.findProperty("composeCompilerReports") == "true") { freeCompilerArgs += listOf( "-P", "plugin:androidx.compose.compiler.plugins.kotlin:reportsDestination= ${project.buildDir.absolutePath}/compose_compiler" ) } } }
  13. Setup to Compose Compiler Metrics in build.gradle for root level

    project tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach { kotlinOptions { if (project.findProperty("composeCompilerMetrics") == "true") { freeCompilerArgs += listOf( "-P", "plugin:androidx.compose.compiler.plugins.kotlin:metricsDestination= ${project.buildDir.absolutePath}/compose_compiler" ) } } }
  14. Run the tasks in Terminal → For compiler report ./gradlew

    assembleRelease -PcomposeCompilerReports=true → For compiler metrics report ./gradlew assembleRelease -PcomposeCompilerMetrics=true
  15. Example Compiler Report Outputs • <modulename>-classes.txt: A report on the

    stability of classes in this module. • <modulename>-composables.txt: A report on how restartable and skippable the composables are in the module. • <modulename>-composables.csv:A CSV version of the composables report that you can import into a spreadsheet or processing using a script
  16. Serve as “scope” where recomposition can start if the function's

    parameters haven't changed from their previous values, Compose will skip the function Restarable Skippable Compose Functions Stability
  17. An object whose properties remain constant after it's created A

    type that is mutable, but the Compose runtime will be notified when its public properties change Immutable Stable Parameters Stability None of the above Unstable
  18. restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun AppIntroContent( stable contents: ImmutableList<Content> stable target:

    SnapshotStateMap<String, AppIntroTargetData> stable onRetry: Function0<Unit>? = @static {} }
  19. • It make as small as possible • remove unused

    code and resources. Compose is a library so we need to take advantages with baseline profile to improve startup performance Use R8 mode Take advantages using Baseline Profile Covering your bases • What to show • Where to place it • How to render it Compose Phases Compose determines the stability of your Composables to determine if they can be skip Diagnose & fix for Stability Issues • Something remember • A key piece • Deriving change • Defer Reading state Compose Best Practices
  20. Connect to me Thaw Zin Toe (P Tut) Mobile Developer

    - Android @ Seven Peaks Facebook - Pe Tut Linkedin - https://www.linkedin.com/in/thaw-zin-toe-35415b197/ Medium - https://medium.com/@thawzintoe For slide