Slide 1

Slide 1 text

AS Poet Optimise your Android Studio builds Boris Farber Google @borisfarber

Slide 2

Slide 2 text

About me ● Android/Java/Kotlin ● Partner Engineer at Google ● Focusing on Android app optimizations (ClassyShark, AS Poet)

Slide 3

Slide 3 text

Disclaimer If you have a Android small app, forget these slides

Slide 4

Slide 4 text

Optimize your Android build

Slide 5

Slide 5 text

Some metrics we have seen ● From 5% to 50% build improvement time ● Disclaimer ⇒ Heavily depends on ○ Project ○ Team skills ○ Etc… ● Take these number with a huge grain of salt

Slide 6

Slide 6 text

Why ● You work relentlessly on your awesome Android app. You add new features, libraries and frameworks. ● Some more code here, some more resources there, it all ends up impacting the build time.

Slide 7

Slide 7 text

Why ● But it doesn’t have to be that way! ● As you refactor your classes and methods, why not to treat your Android project as a refactor-able unit

Slide 8

Slide 8 text

Many variables that affect our build speed ● Annotations ● Resources ● Java Modules ● Android modules ● Number of classes ● Number of methods ● Dependencies ● Topology ● …

Slide 9

Slide 9 text

What ● It requires a lot of testing, experiments, manual work, and patience.

Slide 10

Slide 10 text

AS Poet ● What if we will generate many synthesized Android projects with a configurable number of metrics that mimics your project ● Then we analyze the build times and understand where are the places to refactor

Slide 11

Slide 11 text

AS Poet ● A desktop based utility for Android developers ● Generates synthesized Android projects with a configurable number of metrics

Slide 12

Slide 12 text

How ● Let's take a simple JSON file (less than 20 lines), that mimics the Android projects with gradle plugin syntax ● Has number of other parameters such as class/method count ... ● AS Poet will read the script and generate a buildable Android Studio project

Slide 13

Slide 13 text

No content

Slide 14

Slide 14 text

No content

Slide 15

Slide 15 text

No content

Slide 16

Slide 16 text

No content

Slide 17

Slide 17 text

Dependencies.dot digraph GeneratedASProject { androidAppModule0 -> androidAppModule1, module0, module1; androidAppModule1; module0; module1 -> module0; }

Slide 18

Slide 18 text

Sample Script - definitions { "projectName": "genny", "root": "./modules/", "gradleVersion": "4.3.1", "androidGradlePluginVersion": "3.0.1", "kotlinVersion": "1.1.60", "numModules": "5",

Slide 19

Slide 19 text

Sample Script - source data "allMethods": "4000", "javaPackageCount": "20", "javaClassCount": "8", "javaMethodCount": "2000", "kotlinPackageCount": "20", "kotlinClassCount": "8", "androidModules": "2",

Slide 20

Slide 20 text

Sample Script - dependencies & build types "numActivitiesPerAndroidModule": "8", "dependencies": [ {"from": 3, "to": 2}, {"from": 4, "to": 2}, {"from": 4, "to": 3} ], }

Slide 21

Slide 21 text

Github https://github.com/android/android-studio-poet

Slide 22

Slide 22 text

Features ● GUI and CLI ● Share resources between Android modules (layouts ...) ● Call Java/Koltin code between different modules ● Build flavours ● Topologies ● Full format for fain gained control over modules ● ...

Slide 23

Slide 23 text

Optimize Kotlin on desktop

Slide 24

Slide 24 text

Swing ● Zero Kotlin documentation ● All documentation (really good) is 20 years old ● Ended up doing skeleton development in Java and convert to Kotlin

Slide 25

Slide 25 text

AS Poet ● A desktop based utility for Android developers ● IO bound app (generates folders with 100K with interconnected dependencies)

Slide 26

Slide 26 text

Optimizations ● Measure, measure and measure ● It is better to solve the right problem the wrong way than the wrong problem the right way ● Our architecture - coroutunes for module creation with fork/join for individual classes write

Slide 27

Slide 27 text

Coroutines ● Used at module & project generation generation ● 5x overall improvement

Slide 28

Slide 28 text

Projects val timeModels = measureTimeMillis { ModuleBlueprintFactory.initCache() runBlocking { val deferredModules = projectConfig.pureModuleConfigs.map { async { ModuleBlueprintFactory.create(it, projectRoot) } } val deferredAndroid = projectConfig.androidModuleConfigs.map { async { ModuleBlueprintFactory.createAndroidModule(projectRoot, it, projectConfig.moduleConfigs) } } temporaryModuleBlueprints = deferredModules.map { it.await() } temporaryAndroidBlueprints = deferredAndroid.map { it.await() } } }

Slide 29

Slide 29 text

Source modules fun generate(projectBlueprint: ProjectBlueprint) = runBlocking { fileWriter.delete(projectBlueprint.projectRoot) fileWriter.mkdir(projectBlueprint.projectRoot) ...

Slide 30

Slide 30 text

Parallelisation ● Creating modules in parallel ● Using fork/join

Slide 31

Slide 31 text

Fork/Join var randomCount: Long = 0 projectBlueprint.androidModuleBlueprints.forEach{ blueprint -> val random = Random(randomCount++) val job = launch { androidModuleGenerator.generate(blueprint, random) println("Done writing Android module " + blueprint.name) } allJobs.add(job) } for (job in allJobs) { job.join() }

Slide 32

Slide 32 text

Cache immutable ● Blueprints for methods are immutable ● A lot of small objects ● Caching into deferred maps (inside coroutine)

Slide 33

Slide 33 text

IO Optimizations 101 ● Download only necessary data (gradle full versus gradle bin) ● Close to 33 MBs saved

Slide 34

Slide 34 text

Dependency Injection ● Injector class ● All the objects created there ● Dependencies are passed as parameters ● Maintenance ● Testability

Slide 35

Slide 35 text

ImageIO ● Great & simple API ● Treat your users :)

Slide 36

Slide 36 text

Pretty easy to generate

Slide 37

Slide 37 text

Build - FatJar/Gradle ● FatJar is a gradle task that creates a self contained Jar file with all dependencies

Slide 38

Slide 38 text

Tests ● Should have more ● Selecting the right mocking framework

Slide 39

Slide 39 text

Documentation ● Make it easy to extend (architecture) ● Provide bullet point steps

Slide 40

Slide 40 text

Thank you ! @borisfarber