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

Beyond Modularization: Scaling your Android Bui...

Beyond Modularization: Scaling your Android Build with Gradle

Tips and tricks on how to scale your Android build with Gradle.
How to measure performance improvements and gain insights using Gradle Enterprise.

https://gradle.com/enterprise/trial/

Video of this talk is available here: https://vimeo.com/430648415

Nelson Osacky

June 18, 2020
Tweet

More Decks by Nelson Osacky

Other Decks in Programming

Transcript

  1. Me • Previously Android Engineer • Large projects • SoundCloud

    • Square • Small startups • Solutions Engineer at Gradle
  2. Me • Gradle Plugin Maintainer • Fladle - Easily Scale

    Instrumentation Tests on Firebase https://github.com/runningcode/fladle • Delect - Replace Dagger with Dagger Reflect https://github.com/soundcloud/delect/ • Gradle Doctor - Actionable Insights for your build https://github.com/runningcode/gradle-doctor
  3. Cost of Builds 60s waste * 50 builds / day

    * 50 devs = 42 hours lost / day
  4. Cost of Builds 60s waste * 50 builds / day

    * 50 devs = 42 hours lost / day not including lost focus https://gradle.com/roi-calculator
  5. Cost of Builds 60s waste * 50 builds / day

    * 50 devs = 42 hours lost / day hire 5 new people without paying them! no recruiting https://gradle.com/roi-calculator
  6. Build Speed is Tech Debt And it always pays off

    And is easy to justify working on it
  7. Update All The Things ⬢ Gradle 6.5 ⬢ Android Gradle

    Plugin 4.0.0 ⬢ Gradle Enterprise Plugin 3.3.4 ⬢ Kotlin 1.3.72 ⬢ Third party plugins ⬢ Third party libraries New Performance APIs Caching Improvements Task Configuration Avoidance Background Scan Uploads Incremental Annotation Processors Compiler Perf Improvements
  8. Update All The Things plugins { id "com.github.ben-manes.versions" version "0.28.0"

    } ./gradlew dependencyUpdates -Drevision=release https://github.com/ben-manes/gradle-versions-plugin
  9. Build Lifecycle Initialization Sets up the environment for the build

    and determines which projects will take part in it.
  10. Build Lifecycle Configuration All build scripts of all projects are

    executed. Constructs and configures the task graph.
  11. BUILD SUCCESSFUL in 5s 359 actionable tasks: 3 executed, 356

    up-to-date Publishing build scan... https://gradle.com/s/yj5jtd4kh6ucc Build Analyzer results available
  12. BUILD SUCCESSFUL in 5s 359 actionable tasks: 3 executed, 356

    up-to-date Publishing build scan... https://gradle.com/s/yj5jtd4kh6ucc Build Analyzer results available Gradle Build Scans Android Studio Build Analyzer
  13. Module A Module B Module C dependencies { implementation project('module-b')

    implementation project('module-c') } dependencies { implementation project('module-c') }
  14. Module A Module B Module C dependencies { implementation project('module-b')

    } dependencies { api project('module-c') } module-c must always used when consuming module-b
  15. change_api_dep { title = "Change using api dependency" tasks =

    ["app:k9mail:assembleDebug"] } performance.scenarios Gradle Profiler
  16. change_api_dep { title = "Change using api dependency" tasks =

    ["app:k9mail:assembleDebug"] apply-non-abi-change-to = "openpgp-api/src/main/java/org/ openintents/openpgp/OpenPgpApiManager.java" } performance.scenarios Gradle Profiler
  17. change_api_dep { title = "Change using api dependency" tasks =

    ["app:k9mail:assembleDebug"] apply-non-abi-change-to = "openpgp-api/src/main/java/org/ openintents/openpgp/OpenPgpApiManager.java" } performance.scenarios Gradle Profiler
  18. change_api_dep { title = "Change using api dependency" tasks =

    ["app:k9mail:assembleDebug"] apply-non-abi-change-to = "openpgp-api/src/main/java/org/openintents/openpgp/ OpenPgpApiManager.java" } change_impl_dep { title = "Change using impl dependency" tasks = ["app:k9mail:assembleDebug"] apply-non-abi-change-to = "openpgp-api/src/main/java/org/openintents/openpgp/ OpenPgpApiManager.java" } performance.scenarios Gradle Profiler
  19. change_impl_dep { title = "Change using impl dependency" tasks =

    ["app:k9mail:assembleDebug"] apply-non-abi-change-to = "openpgp-api/src/main/java/org/ openintents/openpgp/OpenPgpApiManager.java" } performance.scenarios Gradle Profiler
  20. change_impl_dep { title = "Change using impl dependency" tasks =

    ["app:k9mail:assembleDebug"] gradle-args = ["-PuseImpl"] apply-non-abi-change-to = "openpgp-api/src/main/java/org/ openintents/openpgp/OpenPgpApiManager.java" } performance.scenarios Gradle Profiler
  21. change_impl_dep { title = "Change using impl dependency" tasks =

    ["app:k9mail:assembleDebug"] gradle-args = ["-PuseImpl"] apply-non-abi-change-to = "openpgp-api/src/main/java/org/ openintents/openpgp/OpenPgpApiManager.java" } performance.scenarios Gradle Profiler
  22. change_api_dep { title = "Change using api dependency" tasks =

    ["app:k9mail:assembleDebug"] apply-non-abi-change-to = "openpgp-api/src/main/java/org/openintents/openpgp/ OpenPgpApiManager.java" } change_impl_dep { title = "Change using impl dependency" tasks = ["app:k9mail:assembleDebug"] gradle-args = ["-PuseImpl"] apply-non-abi-change-to = "openpgp-api/src/main/java/org/openintents/openpgp/ OpenPgpApiManager.java" } performance.scenarios Gradle Profiler
  23. change_api_dep { title = "Change using api dependency" tasks =

    ["app:k9mail:assembleDebug"] apply-non-abi-change-to = "openpgp-api/src/main/java/org/openintents/openpgp/ OpenPgpApiManager.java" } change_impl_dep { title = "Change using impl dependency" tasks = ["app:k9mail:assembleDebug"] gradle-args = ["-PuseImpl"] apply-non-abi-change-to = "openpgp-api/src/main/java/org/openintents/openpgp/ OpenPgpApiManager.java" } performance.scenarios Gradle Profiler
  24. Build Time (milliseconds) 0 3250 6500 9750 13000 #1 #2

    #3 #4 #5 #6 #7 #8 #9 #10 Change using api dependency Change using impl dependency
  25. scenario Change using api dependency Change using impl dependency mean

    11603.0 10151.7 median 11529.5 10049.5 confidence 0.0 99.98429477157690
  26. gradle-profiler --profile buildscan --scenario-file performance.scenarios * Results written to /Users/no/workspace/k-9/profile-out-14

    Scenario Change using api dependency using Gradle 6.5 - Build scan for measured build #1: https://gradle.com/s/ozev5czodllsk Scenario Change using impl dependency using Gradle 6.5 - Build scan for measured build #1: https://gradle.com/s/rxujhuyxqlybu
  27. w: [kapt] Incremental annotation processing requested, but support is disabled

    because the following processors are not incremental: androidx.room.RoomProcessor (NON_INCREMENTAL).
  28. android { ... defaultConfig { ... javaCompileOptions { annotationProcessorOptions {

    arguments += [ "room.schemaLocation":"$projectDir/schemas".toString(), "room.incremental":"true"] } } } }
  29. android { ... defaultConfig { ... javaCompileOptions { annotationProcessorOptions {

    arguments += [ "room.schemaLocation":"$projectDir/schemas".toString(), "room.incremental":"true"] } } } }
  30. plugins { id "com.github.plnice.canidropjetifier" version "0.5" } Can I drop

    Jetifier? ./gradlew -Pandroid.enableJetifier=false canIDropJetifier
  31. Flavor Explosion 2 BuildTypes * 3 BuildFlavors = 6 Variants

    6x code 6x tasks 6x resources 6x cache utilization
  32. def prop = project.findProperty("enabledBuildVariant") ?: "freeDebug" android.variantFilter { variant ->

    if (variant.name != prop) { variant.ignore = true } } ./gradlew assemblePaidDebug -PenableBuildVariant=paidDebug
  33. if (engBuild) { android.variantFilter { variant -> if (variant.name ==

    'release') { variant.ignore = true } } } Disable unused flavors in library modules
  34. if (engBuild) { android.variantFilter { variant -> if (variant.name ==

    'release') { variant.ignore = true } } } Disable unused flavors in library modules
  35. if (engBuild) { android.variantFilter { variant -> if (variant.name ==

    'release') { variant.ignore = true } } } Disable unused flavors in library modules
  36. if (engBuild) { android.variantFilter { variant -> if (variant.name ==

    'debug') { variant.ignore = true } } } Disable unused flavors in library modules
  37. android.variantFilter { variant -> if (variant.name == 'debug') { variant.ignore

    = true } } Disable unused flavors in library modules android.debug.matchingFallbacks = ['release'] in application module
  38. android.variantFilter { variant -> if (variant.name == 'debug') { variant.ignore

    = true } } Disable unused flavors in library modules
  39. Disable debug variant Disable debug variant and AGP features Disable

    AGP features Standard build 0.0 200.0 400.0 600.0 800.0 Avg Configuration Time (ms) Average of 10 builds help using Gradle Profiler
  40. engBuild Ideas •Higher minSdk •Disables Multidex •Disable coreLibraryDesugaring •Strip other

    languages •Strip large images •Strip out native libraries •Try new versions of AGP https://medium.com/@runningcode/testing-new-versions-of-the-android-gradle-plugin-ea80df978316
  41. Build Bigger, Better: Gradle for Large Projects Google I/O 2019

    Aurimas Liutikas and Xavier Ducrohet https://www.youtube.com/watch?v=sQC9-Rj2yLI
  42. The Secrets of the Build Scan Plugin and the Internals

    of Gradle Virtual Android Makers Paris 2020 Me https://www.youtube.com/watch?v=lgaqS0pmUzk