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

The (build) time of my life

The (build) time of my life

Lorenzo Quiroli

October 06, 2018
Tweet

More Decks by Lorenzo Quiroli

Other Decks in Technology

Transcript

  1. About me: Lorenzo Quiroli Born in Cremona (close to here!)

    Senior Android Engineer @ busuu, London Loves to focus on tooling and performances @lorenzoquiroli
  2. Spend time to make your build faster You have more

    free time Dedicate time to optimise your build time It takes less time to build features ∞ time
  3. Android Studio, Gradle and Android Gradle Plugin • Android Studio

    (AS) is the IDE, the software we use to write code, refactor and perform many other daily operations • Gradle is the build system, it’s independent from Android, it can be used for Java-only project and many other builds. • Android Gradle Plugin (AGP) is a custom plugin for Gradle, it provides tasks and configurable settings for your Android build. You could use Android Studio 3.2 with AGP 3.1.4 and Gradle 4.10.2 in your build, that’s fine.
  4. The relationship between AS and AGP AS 3.2.0 AGP 3.2.0

    AS 3.2.0 AGP 3.1.1 AS 3.1.4 AGP 3.2.0
  5. The relationship between Gradle and AGP Way more relaxed relationship,

    but AGP enforces a minimum Gradle version to use. Android Gradle Plugin 3.3.0 requires Gradle 4.10 or bigger
  6. Different features in different updates The Navigation editor is an

    Android Studio feature (introduced in Android Studio 3.2) Incremental Annotation Processing is a Gradle feature (introduce in Gradle 4.7) The new DEX compiler (D8) is a Android Gradle Plugin feature (introduced in AGP 3.1 as experimental, default in AGP 3.2)
  7. Keep them up to date! Don’t use obsolete tooling There

    are improvements in the latest version you’re not taking advantage of For example, thanks to AGP 3.2.0 and D8 the desugaring of Java 8 languages features is way faster
  8. Before fixing, you need to know what’s the problem .

    /gradlew assembleDebug --scan Analyse the result and identify the issues: configuration, execution, parallelism If you want more data, take a look at the Gradle Profiler https://github.com/gradle/gradle-profiler
  9. Configuration Initial phase of every build, where the projects object

    are configured and the build script are executed. It should last around 1 seconds, better if less. There’s currently a bug in the Kotlin plugin which slows this phase down Configuration is redone every time but since Gradle 4.9 there’s an API for avoidance (not supported by AGP yet)
  10. Execution The tasks subset calculated during the configuration phase is

    executed. This is the step where your project is actually building. Focus your analysis here and check parallelism and anything you might find strange
  11. gradle.properties Usually it’s in the root folder of your project

    or in `~/.gradle` Keep it in the root folder and put it under version control org.gradle.parallel=true org.gradle.configureondemand=true org.gradle.daemon=true org.gradle.caching=true org.gradle.jvmargs=-Xmx4g -XX:MaxPermSize=1g
  12. Modules Not just for build time, they are great from

    a software engineering prospective Better separation of concerns, more clear dependency structure If your app grows, modules scale with it Modules are the key for compilation avoidance Use implementation instead of api
  13. Why packaging by feature All the closely related files are

    together If you working on a particular feature, you’re likely gonna change the code in just one module It scales better
  14. Refactoring into modules Takes a lot of time if you

    haven’t think about going multimodule from the beginning Asking product manager the time to create an Instant app / App bundles can help you getting the time
  15. Annotation processors Keep only the essential: Autovalue: replace with Kotlin

    data class Butterknife: replace with findViewById Dagger: ……….. Keep it!
  16. Balancing build time and best practises Best build time Java

    only project Koin (?) Best code quality Kotlin Dagger
  17. Kotlin The more you have inside a module, the faster

    it is! An Android module still cannot be 100% Kotlin unfortunately BuildConfig.java, R.java + Java generated code
  18. Further improvements in AS 3.3 Android Studio 3.3 (since alpha-12)

    generates a R.jar instead of R.java A virtual R class is generated by the IDE to make the developer experience seamless Try new AGP version in a branch of your project!
  19. Butterknife Reflect dependencies { if (properties.containsKey('android.injected.invoked.from.ide')) { implementation 'com.jakewharton:butterknife-reflect:<version>' }

    else { implementation 'com.jakewharton:butterknife:<version>' kapt 'com.jakewharton:butterknife-compiler:<version>' } }
  20. Butterknife Reflect Builds generated from Jenkins / CI will use

    the annotation processor Builds generated from the IDE will use reflection, slower at runtime but it doesn’t matter for debug builds
  21. Butterknife Reflect: time saved No butterknife processor - run 1:

    74 seconds - run 2: 73 seconds - run 3: 79 seconds Butterknife processor: - run 1: 100 seconds - run 2: 90 seconds - run 3: 88 seconds
  22. The argument against Dagger I need to write a lot

    of boilerplate code It’s making my build slow It’s too complicated for what it does
  23. Get rid of the boilerplate! Prefer constructor injection to @Provides

    annotated method class Presenter @Inject constructor(private val repo: SomeRepo) Keep the @Provides for the classes you don’t control Keep modules abstract, avoid passing parameter into the constructor (don’t keep state in there) Use @Binds when you need to bind a implementation to it’s interface type
  24. @Binds @Module abstract class ExerciseActivityBinding { @Binds abstract fun exerciseView(impl:

    ExerciseActivity): ExerciseView } @Component(modules = [ExerciseActivityBinding::class]) interface ExerciseComponent
  25. @Binds makes your UI tests clearer @Module abstract class MockRepositoryBinding

    { @Binds abstract fun repository(impl: FakeRepo): Repository } @Component(modules = [MockRepositoryBinding::class]) interface MockApplicationComponent Don’t override provides methods in modules!
  26. @BindsInstance @Component.Builder Interface Builder { @BindsInstance fun bindApp(app: MyApp): Builder

    = app fun build(): AppComponent } DaggerApplicationComponent.builder() .bindApp(this) .build()
  27. Let’s talk about Dagger Android override fun onCreate(state: Bundle?) {

    ... (context.applicationContext as MyApp).applicationComponent .getThisActivityComponent(ThisActivityModule()) .inject(this) }
  28. Let’s talk about Dagger Android @Module abstract class FeatureModule {

    @ContributesAndroidInjector(modules = [ThisActivityBinding::class]) abstract fun thisActivity(): ThisActivity } @Module abstract class ThisActivityBinding { @Binds abstract fun thisView(impl: ThisActivity) : ThisView }
  29. Let’s talk about Dagger Android After migrating to Dagger Android

    the code quality of my app increased a lot. And the build time… also increased.
  30. Where’s the catch? For each @ContributesAndroidInjector, Dagger generates a subcomponent.

    Subcomponents are generated inside the component that owns them. That meant a lot more code inside the main component in my case Check the LoC of your DaggerApplicationComponent!
  31. Rebuilding it from scratch Isolate the core components of the

    app, used in multiple modules, into their own module (notification, base_ui, api, audio…) Create base_di module with a BaseComponent. This is gonna be your main component across the app. In each feature module, have a FeatureComponent who depends on BaseComponent.
  32. Combining it with Dagger-Android Right now it’s a bit hacky

    (you have to expose the bindings map in the components, merge them manually in the Application and create your own DispatchingAndroidInjectors… You might want to avoid doing that for now
  33. Few improvements are coming... A pull request merged around ~20

    days ago adds support for incremental annotation processing (introduced in Gradle 4.7) Generation of subcomponents ahead of time “Project Bytepoet”: generating bytecode directly instead of Java files (takes around 25% of the time + better for Kotlin!)
  34. Last but not least Convince your CTO to buy you

    a better computer Delete the code you don’t use anymore
  35. Useful Links - Gradle build optimisation - https://goo.gl/hXqKWm - Issue

    from the Gradle team - https://goo.gl/aUBQVs - Jake Wharton talk at Droidcon London - Join Android Developers Italia - https://androiddevs.it/