Loading...
Home
  • Tech Blogs
  • Videos
  • Conferences
    • Droidcon News
    • Upcoming Conferences
    • Become a Partner
    • Past Events
    • Keep Me Informed
    • Diversity Scholarships
  • Community
    • droidcon Team
    • How to Hold a Droidcon
  • Android Careers
Sign In

Global CSS

 

Gradle Webinar Q&A Follow-Up

 

 
Greg Fawson
CEO @ droidcon Global
Published: June 22, 2020
Tweet
Share
 

 

On Thursday, June 18th, we had the privilege to have Nelson Osacky from Gradle present "Not Just Another Modularization Talk: Scaling your Android Build with Gradle". Here is the link to the presentation. Following his presentation we had a fantastic panel discussion with John Rodriguez from Square, Zac Sweers from Slack, Ivan Gravilovich from Google and Israel Camacho from Dropbox. We had a fantastic discussion, but didn't get to all the great questions from our Slido feed.  Many thanks to all our panelists who took time out after the webinar to answer all your questions. Below you will find the questions and the answers to all the unanswered questions. 

Also...if you want to learn more about the topic discussed, our friends at Gradle have put together some more in-depth training that you can register for:

Build Cache Deep Dive Training:

https://gradle.com/training/build-cache-deep-dive/?time=1592524800 

DevProd Engineering Training:

https://gradle.com/training/developer-productivity-workshop/?time=1594252800 

 

Webinar Q&A


From Carlo
Q: Any legal way to read the running buildType (and maybe flavor when running android) to enable/disable that "engBuild" property you have shown earlier?

A: Jrod: 

>

android.applicationVariants.configureEach { variant ->
  variant.buildType

}

or

android.libraryVariants.configureEach { variant ->

  variant.buildType

}

 

Anonymous
Q: Do you keep org.gradle.caching=true for local builds? I see a lot of my builds fail when I have it turned on especially when the change is Dagger related.

A: Jrod: Yes,  but I think it's on by default in recent Gradle releases?  Occasionally, plugins will corrupt the cache, which can be annoying.  In that case, disable the setting, file an issue, and re-enable once fixed.

A: Icamacho: yes and yes fails might happen from time to time but the average case you will gain speed in the build

A: Zac: +1 to what the others have said. If you’re worried about having a dirty cache, you can run with --rerun-tasks to force a cache-less build and replenish the cache with the new result.

 

Anonymous
Q: generateBuildConfig takes >20s for one of my modules. what I can do to figure out why it's taking that long?

A: Try to generate a build scan to get full build information. To gain more insights, use gradle-profiler to generate a flamegraph for a build running only that task.

A: Jrod: another possible approach: our team disables this task for library modules and instead inject the placeholder values from the app module using DI.  AGP 4.0 offers a way  to do this more easily using the buildFeatures {} block.

A: Zac: we do the same as jrod suggested. Only app has a buildconfig generated. 20 seconds sounds like a bug, gather build scan info and file a bug with the link/details!

 

Efeturi Money
Q: Does taskProvider.configure {...} run in configuration phase? for tasks using the provider API is calling input.set(provider.get) diff from input.set(provider)

A: taskProvider.configure { … } runs during the configuration phase if the task will be executed in the build, or eagerly resolved. When setting task inputs, you should avoid resolving providers. "input.set(provider.get)" does not benefit from lazy configuration, and you should use "input.set(provider)".

 

Efeturi Money
Q: What exactly made the `witness` plugin slow? Can you show some code that details what they were doing wrong?

A: Nelson: It was verifying the checksum of all dependencies in the configuration phase. You probably don’t need to run this on every build, just release builds.
Here is the source: https://github.com/signalapp/Signal-Android/blob/master/buildSrc/src/main/groovy/org/whispersystems/witness/WitnessPlugin.groovy
Also, this dependency verification now a built-in feature to Gradle: https://docs.gradle.org/current/userguide/dependency_verification.html

 

Tobi
Q: Running assembleDebug twice results in 8 tasks being executed. What next? How to kill them?

A: Some tasks are lifecycle tasks, which means they are essentially no-op, and they exist as a convenience to e.g. assemble an app or a library, or run all tests. For those tasks, it is ok if they are running. However, if a task that actually produces outputs runs, it is an issue.

A: Jrod: to determine which tasks are running, pass --console=plain and see which tasks are not UP_TO_DATE or _NO_SOURCE.  If the # of tasks are low, but fast, this should be ok.
A: A build scan will tell you the reasons why these tasks were executed based on which inputs changed. Try a build scan: https://scans.gradle.com/

 

Anonymous
Q: When is afterEvaluate block executed? Which phase?  (edited) 
 

A: At the end of the configuration phase, once the build.gradle files are evaluated, in the order of which each block was registered.

 

Svyatoslav
Q: If we disable plugins in local build, does it mean that remote build cache will be incompatible? Assuming that remote build cache is pushed by CI.

A: This would depend if the plugins are modifying inputs to the tasks. One way to verify this is to use task input comparison in Gradle Enterprise.

 

Anonymous
Q: On CI it's easy to have memory issues when running tests - what's the best strategy to manage memory between tasks that spawn java processes and Gradle daemon?

A: Jrod: It depends.  Test sharding can help.  Giving the daemon more heap can help.  Reducing test framework dependencies (e.g., Robolectric) can help.  Ensuring that Setup/Teardowns aren't leaking  memory between tests can help.

A: Zac: Here’s a magic shell command: `jps|grep -E 'KotlinCompileDaemon|GradleDaemon'| awk '{print $1}'| xargs kill -9` and also the classic `killall java`

 

Jason
Q: Which phase downloads dependencies? Configuration or Execution?

A: Jrod: If the dependency is a buildscript dependency (a dependency of a plugin or an inline build script or buildSrc), then the dependency will be downloaded in the configuration phase.  Otherwise, in recent versions of Gradle/AGP, dependency resolution (which includes downloading) will be deferred to execution of the task that requires the dependency (usually, compilation tasks).

A: Nelson: Yes jrod is right. In addition, bad behaving plugins or build scripts can also cause dependencies to be resolved during the configuration phase. When a dependency is resolved it would trigger the download. You can see time spent downloading dependencies as well as which dependencies were resolved in the configuration phase in build scans..

A: Ivan: Just a tinny add-on here. In some cases you may see your build showing configuration phase as complete (at 100% in the terminal), and the execution phase has not started yet. This is a phase where the task graph is built, and in some cases plugins may trigger dependency resolution (e.g. https://issuetracker.google.com/156449751). However, this should not be a common case.

 

Anonymous
Q: If you run assembleDebug, are you building only debug flavours of the modules? or you are building all flavours? I am not sure what you said about this

A: Jrod: Yes, by running assembleDebug, you are only building debug flavors which includes the main and debug source sets and debug variants of external binary dependencies, if set up that way.

A: Zac: To add an example - if you have debug/release build types with internal/external flavors, then “assembleDebug” will expand to run both “assembleInternalDebug” and “assembleExternalDebug”.

Joe
Q: For the apps you all work on with 100+ modules, how big is each module? Too many small modules can add lots of configuration time, no?

A: Jrod: Size/LOC varies across all modules.  Depending on the applied plugins, more modules can add more configuration time.  Gradle has quoted around 200ms (fact check me) around per module in an ideal setup.  Configuration caching + incremental compilation + compilation avoidance will help here, since clean builds should not happen very often when things are working correctly!

 

Eugen
Q:Why even Gradle people use groovy GString instead of String?

A: Because string interpolation is powerful.

Anonymous
Q: What are some strategies for having code generated at build time (for example, a constant with the git commit) to reduce how much needs to be rebuilt?

A: Jrod: Defer the computation of the task.  Xavier from Google Tools team suggests one way here.  Or create a task whose doLast {} block performs the operation and wire the task dependency such that it executes after any dependent tasks.

A: Zac: To add to Jrod’s answer, use gradle providers to defer computation of the inputs for task properties. For things like env vars, gradle properties, etc, gradle also has new APIs for provider wrappers of these in gradle 6.5.

A: Nelson: You may also want to write resource strings instead of code. This may be faster for your build. Something to experiment with.

Anonymous
Q: What is the meaning of "kotlin.parallel.tasks.in.project=true" in contrast with "org.gradle.parallel=true"

A: Jrod: The former is a Jetbrain kotlin plugin property, whereas the latter is a Gradle framework property.  Gradle introduced parallel builds a while back for plugin authors to leverage, and the Kotlin team eventually enables this feature, representing by using different feature flags.

A: Zac: The kotlin property, in particular, allows for parallel execution of kotlin tasks within the same project. If you assemble multiple variants in an android project with multiple kotlin compile tasks, these would normally operate serially without this property. With it enabled, it will use the gradle Worker API to run them in parallel.

 

Anonymous
Q: Any recommendation how to define a "module", is it a page? is it a feature? sometimes it's hard to communicate this to engineers

A: Jrod: In Gradle speak, it's a bundle of related code.  How one decides to organize their modules is entirely up to them.  However, I tend to have several types of modules: feature modules,  utils/commons modules, widgets, then of course the app modules.  Btw, a feature can have more than one module!  For example, feature:views, feature:presenters, feature:fixtures, feature:viewmodels can all relate to each other in a micro-ecosystem, while only a few will actually be referenced by the monolith :app module.

A: Zac: At Slack, we generally define them by feature or as a focused library. Sometimes several modules can come as a package deal, where one is an API module and the others are implementations of it.

 

Anonymous
Q: If tasks are run in Configuration phase, what's the point of Execution phase?

A: Jrod: tasks are not run in the configuration phase.  they're set up (i.e., configured) based on DSL or other plugin configuration settings/properties.  this all results in a task graph, then a portion of that graph is walked in order to "execute" the set of tasks.  This becomes more obvious, if say you do this:

E.g,/. gradlew task1 task2

Vs:

E.g,/. gradlew task1; ./gradlew task2

The former only assembles the task graph once and can optimize/reduce the computations needed to satisfy both tasks!

 

Anonymous
Q: Our build sometimes doesn't scale with memory, we keep increasing -Xmx as our project grows, how can optimize?

A: Jrod: this is very project-specific.  sometimes, bugs in Gradle/Kotlin/AGP. sometimes, static leaks in the app.  I've found using the gradle-profiler or VisualVM/other profilers can help narrow down the set of the issues.  Unfortunately, it can be a never-ending battle, since we will always continue to write new code :)

 

Svyatoslav
Q: Do you have a recommendation about Gradle module size in Android app? Let's say when it's bigger than X lines of code it's better to split it. Or vice versa.

A: Jrod: no hard limits on LOC.  on my team, limits tend to be more logical.  

:app 4 years ago.

:app, :features, 2 years ago

:app, :features:views, :features:presenters, 1 year ago

Keeping a fluid approach and breaking stuff up when it makes sense to, is the only general rule we apply.  Moving out of :app is the other main general rule.  It matters less "where it goes" and more important to keep it out of the main path as much as possible especially as teams grow! Once it's out of :app, you can move it around as the needs arise and it should be much easier than the initial pulling of :app.

A: Zac: If you have a logically confined feature or library, there’s no reason to wait. Better to modularize too early than too late. My CatchUp project is small but with 20+ modules, some as small as a few classes. The more you modularize, the less often you have to recompile those less-often-changed ones.

 

Anonymous
Q: What does a typical module layering strategy (features, infrastructure, etc)? How do you break down code into those layered modules and manage it all?

A: Jrod: Layering strategy varies.  Previous answer offers one approach.  It would be nice if the IDE or Gradle offered simpler more-lightweight tooling to create modules, but for now, we have a kotlin script that creates simple folder /src/main/ and shell AndroidManifest.xml files to make module creation cheap and fast!

A: Zac: I highly recommend this talk by Jesse Wilson called “Writing Code That Lasts Forever”: https://www.youtube.com/watch?v=YZstpc2939s

 

Anonymous
Q: Do you ever feel that introducing new custom Gradle tasks / config in your code makes projects harder to maintain (Groovy and Gradle major version API changes)?

A: Jrod: Only when the momentum to stay ahead of what plugins are doing falls behind.  For example, I already have WIP branches on CI for AGP 4.1 and 4.2 that won't merge into master for probably 3 months.  I don't really look at them often.  I bump to latest canary/beta, push  to CI, see it go red and maybe file 1 or 2 issues, this takes 15-30 mins max (and I'll be doing other things in the meantime).  But this surfaces issues earlier, which helps project maintainers either fix things earlier or educate  us on what changes need to be made, which can also help documentation writing!

A: Zac: To add to jrod’s answer, I highly recommend setting up CI shadow jobs that build your project against newer (in-development) versions of your tools (gradle, AGP, kotlin, etc) on a continuous cadence so you can catch issues early. Since we started doing this, we are always confident updating to tools on day one of their stable release. This also is a win for maintainers of those tools, as we can file bugs early.

 

Anonymous
Q: Is there any way to find unused dependencies in build.gradle file?

A: Jrod: I've been using Tony's Robalik's dependency-analysis plugin for this.  It's maturing very nicely and has caught some things we otherwise would have missed!

 

Svyatoslav
Q: How to measure time taken by Jetifier?

A: Jrod: Profiling in "some way" (build scans, --profile, etc.) the Jetifier transform tasks.

A: Zac: Tangentially related, but there was a significant performance issue discovered with Jetifier recently that is fixed in AGP 4.1, but if you’re on 4.0 then it would be a good motivation to move away. We were able to disable jetifier at Slack earlier this year.

 

Anonymous
Q: So we should add kapt for every module using. dagger/jsr-330 annotations?

A: Jrod:: Not necessarily, but if the @Inject is on a class in  that module, then probably yes.

A: Zac: Definitely run it. Better to be safe than sorry, as I have not encountered a case where it shouldn’t be added. Use the dependency-analysis plugin, it will give you an error if you’ve accidentally applied an annotation processor that isn’t necessary 👍

 

Anonymous
Q: how good is evaluate which task should be executed on CI given the git changes ? could be dangerous?

A: Jrod: I'd love to see a general solution to this!  It's only dangerous as any software feature released without copious tests!

A: Zac: If the question is about trying to avoid running certain tests or tasks based on what git files affect, I’ve had an idea about this but it’s very tricky to get right. Buck (or maybe just a tool we had at Uber) attempted to do this for test avoidance and it was a dumpster fire.

 

Anonymous
Q: Do Dynamic Features Module has faster build time compare to normal Android Library.

A: Zac: No. If anything they are slower, because compilation of a dynamic feature depends on the base module, which is not the case for libraries.

A: Nelson: I haven’t done any performance analysis, but I recommend running the Gradle profiler to see if this is the case. https://github.com/gradle/gradle-profiler From some build scans, I’ve noticed that this plugin eagerly configures many tasks in the configuration phase.

 

Anonymous
Q:Laying out screens under the system UI (edge-to-edge) doesn't seem to be widely adopted after it was pushed by Google at I/O. Why do you think that is?

A: Zac: Having just spent a couple weeks dealing with insets, I think it’s a combination of two things: the average product designer is not using an android device day to day and may not even be aware of the option, and the APIs for insets are not fun. A developer interested in using them will face an uphill battle convincing product to care and possibly be demotivated by friction in working with the API.

 

Anonymous
Q: in CI, what is the best practice running parallel execution (not gradle's parallel) at the same project?

A: Jrod: what does non-gradle parallel execution mean here?

A: Zac: Interpreting this as doing your own parallelism, like using coroutines or rxjava inside your plugin. I’ve done this in the past and it’s been fine, the problem really arises in that you are not participating or cooperating with whatever parallelism is going on around it. I.e. if a user asked for no parallelism in the build, you are not respecting that. If gradle is stressing the system, you may negatively affect its ability to load balance fairly. You also have no access to a lifecycle or scope hook, so be careful that your async work doesn’t continue to run if the build is canceled.

 

Abhishek
Q: How does following flags affect build performance on Single core machines. Like a CI Server
org.gradle.parallel=true
kapt.use.worker.api=true

A: Jrod: try not to use single-core CI workers, the benefits of parallelization only come when you can do work in parallel!

A: Nelson: Agree with Jrod, Don’t use single-core CI., The answer would depend on how many gradle workers you have configured for the build, the type of machine it is running on, if the machine has virtual cores, how fast the CPU is and other factors. The best way to know is to measure!

 

Abhishek
Q: How does debugging work with proguard and obfuscation in libraries if only release variant is enabled?

A: Jrod: there's a setting to provide debug mappings to assist debuggers using obfuscated code.

A: Nelson: The Android Gradle plugin does not run proguard by default on library modules. That being said you can enable and disable proguard as you wish for whatever build variant/combination you please.

 

Anonymous
Q: Is supposed to be a problem if we include Gradle module programmatically in settings.gradle?

A: Jrod: i don't understand this question.

A: Zac: Assuming this is about conditionally including modules. This has been fine in my experience.

 

Tags: Gradle, Build, Performance

Android News
Evolution of Android Update SystemEvolution of Android Update System
Evolution of Android Update SystemEvolution of Android Update System

By Ivan Kuten

So, how can you update Android on mobile devices? While developing software for Smart TVs and Android-based set-top boxes, we’ve narrowed it down to four ways, discarding some very exotic options:

By ProAndroidDev -
Android News
Happy Railway
Happy Railway

By Hadi Lashkari Ghouchani

This post is on the tail of Railway Oriented Programming in Kotlin by Antony Harfield. So you need to read it first and continue here. As it’s obvious I really liked it and tried it out. It needs every process have a result like

By ProAndroidDev -
Android News
Unit Tests and Concurrency
Unit Tests and Concurrency

By Stojan Anastasov

Once Retrofit added RxJava support, RxJava became my go-to concurrency framework for writing Android apps. One of the great things about RxJava is the excellent testing support. It includes TestObserver, TestScheduler, RxJavaPlugins so you can switch your schedulers in tests.

By ProAndroidDev -
Android News
When Compat libraries will not save you
When Compat libraries will not save you

By Danny Preussler

And why you should avoid using the “NewApi” suppression! The idea of “Compat” libraries was probably one of the key aspects of Android dominating the mobile space. Other than with iOS, Android users often could not update their operating system after a new version launch, simply as their phones won’t allow them to, the Android problem of fragmentation.

 

By ProAndroidDev -
droidcon News

Tech Showcases,

Developer Resources &

Partners

/portal/rest/jcr/repository/collaboration/Groups/spaces/droidcon_hq/Documents/public/home-details/EmployerBrandingHeader
EmployerBrandingHeader
https://jobs.droidcon.com/
/portal/rest/jcr/repository/collaboration/Groups/spaces/droidcon_hq/Documents/public/employerbranding/jobs-droidcon/jobs.droidcon.com
jobs.droidcon.com

Latest Android Jobs

http://www.kotlinweekly.net/
/portal/rest/jcr/repository/collaboration/Groups/spaces/droidcon_hq/Documents/public/employerbranding/kotlin-weekly/Kotlin Weekly
Kotlin Weekly

Your weekly dose of Kotlin

https://proandroiddev.com/
/portal/rest/jcr/repository/collaboration/Groups/spaces/droidcon_hq/Documents/public/employerbranding/pad/ProAndroidDev
ProAndroidDev

Android Tech Blogs, Case Studies and Step-by-Step Coding

/detail?content-id=/repository/collaboration/Groups/spaces/droidcon_hq/Documents/public/employerbranding/Zalando/Zalando
/portal/rest/jcr/repository/collaboration/Groups/spaces/droidcon_hq/Documents/public/employerbranding/Zalando/Zalando
Zalando

Meet one of Berlin's top employers

/detail?content-id=/repository/collaboration/Groups/spaces/droidcon_hq/Documents/public/employerbranding/Academy for App Success/Academy for App Success
/portal/rest/jcr/repository/collaboration/Groups/spaces/droidcon_hq/Documents/public/employerbranding/Academy for App Success/Academy for App Success
Academy for App Success

Google Play resources tailored for the global droidcon community

Follow us

Team droidcon

Get in touch with us

Write us an Email

 

 

Quicklinks

> Code of Conduct

> Terms and Conditions

> How to hold a conference

> FAQs

> Imprint

Droidcon is a registered trademark of Mobile Seasons GmbH Copyright © 2020. All rights reserved.

powered by Breakpoint One