Android Development
Feature Development Tech Debt
(There are other things too)
Slide 10
Slide 10 text
Tech Debt
Refactoring Build Speed
Slide 11
Slide 11 text
Build Speed is Tech Debt
And it always pays off
Slide 12
Slide 12 text
Cost of Builds
60s waste * 50 builds / day * 50 devs
= 42 hours lost / day
Slide 13
Slide 13 text
Cost of Builds
60s waste * 50 builds / day * 50 devs
= 42 hours lost / day
not including lost focus
https://gradle.com/roi-calculator
Slide 14
Slide 14 text
Fast Builds Matter
Slide 15
Slide 15 text
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
Slide 16
Slide 16 text
Build Speed is Tech Debt
And it always pays off
And is easy to justify working on it
Slide 17
Slide 17 text
Motivated?
Where do we start?
Slide 18
Slide 18 text
No content
Slide 19
Slide 19 text
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
Slide 20
Slide 20 text
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
enabled by default
ext.engBuild = (project.findProperty('engBuild') ?: 'true')
.toBoolean()
Slide 61
Slide 61 text
if (!engBuild) {
apply plugin: 'com.google.firebase.crashlytics'
apply plugin: 'com.google.firebase.firebase-perf'
}
Slide 62
Slide 62 text
./gradlew assembleDebug
./gradlew assembleRelease -PengBuild=false
disable on CI or testing release builds
Slide 63
Slide 63 text
engBuild=false
disable on CI or testing release builds
gradle.properties
Slide 64
Slide 64 text
engBuild pattern
Slide 65
Slide 65 text
16627 tasks is a red flag
Slide 66
Slide 66 text
Use Task Configuration Avoidance
https://docs.gradle.org/current/userguide/task_configuration_avoidance.html
Slide 67
Slide 67 text
api vs implementation
Slide 68
Slide 68 text
Module A Module B Module C
dependencies {
implementation project('module-b')
implementation project('module-c')
}
dependencies {
implementation project('module-c')
}
Slide 69
Slide 69 text
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
Slide 70
Slide 70 text
api vs implementation
convenience vs performance
Slide 71
Slide 71 text
Real World Example
Slide 72
Slide 72 text
https://github.com/k9mail/k-9
K9 Mail App
Slide 73
Slide 73 text
K9 Mail App
https://github.com/k9mail/k-9/blob/master/app/core/build.gradle#L12
apiaproject(':plugins:openpgp-api-lib:openpgp-api')
Slide 74
Slide 74 text
K9 Mail App
https://github.com/k9mail/k-9/blob/master/app/core/build.gradle#L12
apiaproject(':plugins:openpgp-api-lib:openpgp-api')
Slide 75
Slide 75 text
apiaproject(':plugins:openpgp-api-lib:openpgp-api')
K9 Mail App
Slide 76
Slide 76 text
if (project.hasProperty('useImpl')) {
implementation project(':plugins:openpgp-api-lib:openpgp-api')
} else {
apiaproject(':plugins:openpgp-api-lib:openpgp-api')
}
K9 Mail App
Slide 77
Slide 77 text
./gradlew :app:k9mail:assembleDebug
./gradlew :app:k9mail:assembleDebug -PuseImpl
K9 Mail App
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
Slide 102
Slide 102 text
https://scans.gradle.com/s/ozev5czodllsk/timeline?outcomeFilter=SUCCESS
api Dependency
https://github.com/autonomousapps/dependency-analysis-android-gradle-plugin
Dependency Analysis Plugin
plugins {
id "com.autonomousapps.dependency-analysis" version "0.49.0"
}
Slide 119
Slide 119 text
No content
Slide 120
Slide 120 text
Annotation Processors
Slide 121
Slide 121 text
Make sure they are incremental!
Slide 122
Slide 122 text
w: [kapt] Incremental annotation processing
requested, but support is disabled because the
following processors are not incremental:
androidx.room.RoomProcessor (NON_INCREMENTAL).
Flavor Explosion
• Mostly a configuration time win.
• Permanently disable flavors you
don't need.
Slide 165
Slide 165 text
How to cut build times in half
Slide 166
Slide 166 text
How to cut build times in half.
Slide 167
Slide 167 text
apply plugin: 'com.android.library'
Slide 168
Slide 168 text
assembleDebug
assembleRelease
Slide 169
Slide 169 text
2x code
2x resources
2x tasks
2x tests !!!!!
Slide 170
Slide 170 text
How often does the debug and
release code differ for library
modules?
Slide 171
Slide 171 text
./gradlew test
runs all tests twice
Slide 172
Slide 172 text
./gradlew testDebug
java module tests don't run
Slide 173
Slide 173 text
Recompile everything when
switching to release
Slide 174
Slide 174 text
CI takes twice as long
Slide 175
Slide 175 text
if (engBuild) {
}
Disable unused flavors in library modules
Slide 176
Slide 176 text
if (engBuild) {
android.variantFilter { variant ->
}
}
Disable unused flavors in library modules
Slide 177
Slide 177 text
if (engBuild) {
android.variantFilter { variant ->
if (variant.name == 'release') {
variant.ignore = true
}
}
}
Disable unused flavors in library modules
Slide 178
Slide 178 text
if (engBuild) {
android.variantFilter { variant ->
if (variant.name == 'release') {
variant.ignore = true
}
}
}
Disable unused flavors in library modules
Slide 179
Slide 179 text
if (engBuild) {
android.variantFilter { variant ->
if (variant.name == 'release') {
variant.ignore = true
}
}
}
Disable unused flavors in library modules
Slide 180
Slide 180 text
if (engBuild) {
android.variantFilter { variant ->
if (variant.name == 'debug') {
variant.ignore = true
}
}
}
Disable unused flavors in library modules
Slide 181
Slide 181 text
android.variantFilter { variant ->
if (variant.name == 'debug') {
variant.ignore = true
}
}
Disable unused flavors in library modules
android.debug.matchingFallbacks = ['release']
in
application module
Slide 182
Slide 182 text
android.variantFilter { variant ->
if (variant.name == 'debug') {
variant.ignore = true
}
}
Disable unused flavors in library modules
Slide 183
Slide 183 text
Build and test times cut in half
Slide 184
Slide 184 text
Switching to release build doesn't
recompile everything
Slide 185
Slide 185 text
Disable stuff!
#protip
Slide 186
Slide 186 text
Disable stuff!
#protip
measure!
Slide 187
Slide 187 text
./gradlew help
Measure configuration time
Slide 188
Slide 188 text
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
Slide 189
Slide 189 text
783ms to 688ms ~= 13% faster
Slide 190
Slide 190 text
13% configuration time
improvement that scales
Slide 191
Slide 191 text
Regular build
assembleDebug
Slide 192
Slide 192 text
assembleDebug
Debug disabled, AGP features disabled
Slide 193
Slide 193 text
Disable More Things
https://github.com/android/gradle-recipes
Slide 194
Slide 194 text
engBuild Ideas
Slide 195
Slide 195 text
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
Slide 196
Slide 196 text
Always Measure!
Slide 197
Slide 197 text
Upcoming Performance
Enhancements
Slide 198
Slide 198 text
File System Watching
Slide 199
Slide 199 text
Fast input snapshotting
Slide 200
Slide 200 text
File System Watching
https://blog.gradle.org/introducing-file-system-watching
org.gradle.unsafe.watch-fs=true
Slide 201
Slide 201 text
Configuration Caching
Slide 202
Slide 202 text
Configuration Caching
Slide 203
Slide 203 text
Configuration Caching
The build scripts of all projects which are part of the build are
executed.