As members of the Android Developer Tools team, my teammates and I come to work each day to build tools intended to make Android the best mobile developer platform out there. Sometimes, this means building tools that help you write apps. At other times, this means building tools that help you get back to writing your app.
We know that lengthy builds waste time, break up the flow of development, and ultimately generate lots of frustration. The average Android Studio user builds their project ~65 times a week, so even a 10 second slow down would cost around 9 hours of each developer’s time a year. Yet, as was noted in our earlier article, Improving build speed in Android Studio, ~60% of Android developers do not analyze their build. Paired with ever growing project complexity, this has resulted in consistently increasing build durations across all Android Studio projects and a ton of lost productivity.
To address this, we created the Build Analyzer, a tool designed to help all developers understand what determines the duration of their build and how it can be minimized.
Taking advantage of the Build Analyzer
Each time you build your app, Android Studio generates a Build Analyzer report. To see the report, make sure you do the following:
- Upgrade Android Gradle Plugin such that its version is greater than or equal to 4.0.0
- Build or rebuild your project
- Navigate to the Build Analyzer by selecting View > Tool Windows > Build from the menu bar, and then clicking on the Build Analyzer tab
For the sake of this article, we’re going to be walking through a Build Analyzer report generated when running a clean build of the Santa Tracker app, an open-source educational app developed by Google.
Opening the Build Analyzer lands you on the Overview page. The Overview page provides some basic information and links to jump off into the details. At a glance, you can see that this build’s duration was primarily determined by task execution as opposed to configuration.
To learn more, let’s dig into the two datasets provided by the Build Analyzer, Tasks and Warnings by either using the links under Common views into this build or the combobox in the top left corner which currently reads “Overview”.
These tasks, either due to input/output dependencies or parallelism constraints, determined the observed build duration. Excluded from this set are tasks that ran in parallel without impacting observed build duration. Looking at this allows me to tell where the heavy lifting is being done when I build my app.
When viewed in a flat list, tasks are color coded by their source. Tasks originating from Project Customizations are defined locally in your project, and are the easiest to modify. Tasks labeled as coming from Android/Java/Kotlin Plugins come from core plugins, and tasks which were added by Other Binary Plugins could come from a binary plugin that someone on your team has authored or from a third-party binary plugin.
Tasks are displayed in a flat list by default, but can be grouped by the plugin that added them with the Group by plugin control. Grouping adds a node for each plugin in your project and modifies the visualization to display plugins as opposed to individual tasks.
Here, grouping allows me to observe that the majority of my build’s duration is due to the execution of tasks from core plugins, like the Kotlin and Android Gradle plugins. This is unsurprising, especially when executing a clean build of a project like Santa Tracker. Executing an incremental build often results in less time being spent on the execution of tasks from core plugins and can reveal the impact of other plugins.
This plugin view is especially useful when comparing your build times before and after adding or upgrading a plugin, which will allow you to weigh the benefits of any changes against their impact on build duration. Being mindful whenever making a change is one of the best ways to prevent constantly increasing build durations from eating into your productivity. It’s also worth noting that build durations vary due to incremental execution and other constraints, so it’s best to compare a few builds.
Here you can see all warnings generated by this build. This includes warnings generated for tasks that did not determine this build’s duration.
Each warning contains an explanation of why it was generated, a recommendation for how to resolve it, and, if applicable, the details of the task which generated it. In the case that the warning was not generated by someone on your team, you can use the Generate report feature to explain the issue to the original task or plugin author.
As shown in the image above, we generated a Task Setup warning when using the Build Analyzer on the Santa Tracker project discussed above. This issue looks to be due to conflicting output directories, so we can resolve this problem by changing the output directory of either the generateExtraResources or mergeDebugResources tasks. The mergeDebugResources task comes from the base android plugin and cannot be edited locally, while the generateExtraResources task is project customization authored by my team. As a result, i’m going to edit the generateExtraResources task.
Again, note that the above report was generated by a clean build. Reports generated by incremental builds will be less consistent but are likely a better representation of your usual development workflow, so make sure that you use the Build Analyzer with both clean and incremental builds. To learn even more about what you can do with the Build Analyzer, read Troubleshoot build performance.
How the Build Analyzer works
Each time you use Android Studio to build your project, the Build Analyzer uses both the Gradle Tooling API and the Android Gradle Plugin to locally gather data. The Tooling API is used to attach a ProgressListener, which provides information on project configuration, task execution, annotation processors, and more. The Android Gradle Plugin provides additional information on things such as task name to task class name mapping and task configuration.
When your build completes, the Build Analyzer runs this information through a suite of analyzers, each of which looks for and reports a specific problem. As an example, one analyzer checks for tasks that are configured to run on each and every build. These tasks prevent time from being saved during incremental builds and can seriously impact build speed.
How does this analyzer find tasks that always run? A Gradle task is up-to-date when none of its inputs or outputs have changed since the last build. When a task is up-to-date, Gradle skips running the task and re-uses the task’s old outputs instead of wasting time recreating them. A task will never be up-to-date if it doesn’t have both a task input and output declared. This analyzer consumes the information on task outputs which is provided by the Gradle Tooling API and identifies all tasks that do not declare any outputs or that specifically override up-to-date to always be false. In doing so, it identifies all tasks that will never be up-to-date and that will always run. By doing this automatically, we hope to help you quickly address this problem and start taking full advantage of incremental builds.
Alongside the analyzer which identifies tasks that always run, there are currently two additional analyzers. One identifies annotation processors that are not incremental and the other identifies tasks that overwrite each other’s outputs. In the future, we plan to continue releasing analyzers that help you quickly find common problems and learn about Gradle best practices.
If the Build Analyzer reports a warning for a task that is part of a third-party plugin, then consider filing a bug against the original plugin author. These issues may not be addressed immediately but will result in improvements for the entire ecosystem. If you believe that there is an issue with the Build Analyzer itself, please let us know on our issue tracker.
As this tool matures, we hope to expand our suite of analyzers to identify additional warnings. We are also excited to learn how people use the tool and will be making improvements to the navigation, visualizations, and other components accordingly. Please let us know if you have any feedback, especially concerning issues you identified in your own build that were not caught by our analyzers.