惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

M
MIT News - Artificial intelligence
罗磊的独立博客
Hugging Face - Blog
Hugging Face - Blog
J
Java Code Geeks
G
Google Developers Blog
美团技术团队
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
腾讯CDC
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
T
The Blog of Author Tim Ferriss
月光博客
月光博客
B
Blog
WordPress大学
WordPress大学
云风的 BLOG
云风的 BLOG
博客园_首页
人人都是产品经理
人人都是产品经理
aimingoo的专栏
aimingoo的专栏
Y
Y Combinator Blog
Jina AI
Jina AI
S
SegmentFault 最新的问题
H
Help Net Security
博客园 - 聂微东
Microsoft Azure Blog
Microsoft Azure Blog
Google DeepMind News
Google DeepMind News

Dropbox Tech Blog

Testing cookie behavior across hundreds of web surfaces with our in-house auditor Improving infrastructure efficiency for growing demand in the age of AI How our universal content processing platform Riviera evolved for AI and beyond How we used DSPy to turn AI evaluations into better responses in Dash chat How Dropbox uses MCP and Dash to close the design-to-code security gap Beyond code generation: rethinking engineering productivity in the age of AI agents Introducing Nova, our internal platform for coding agents Improving storage efficiency in Magic Pocket, our immutable blob store Reducing our monorepo size to improve developer velocity How we optimized Dash's relevance judge with DSPy Using LLMs to amplify human labeling and improve Dash search relevance How low-bit inference enables efficient AI Insights from our executive roundtable on AI and engineering productivity Engineering VP Josh Clemm on how we use knowledge graphs, MCP, and DSPy in Dash Inside the feature store powering real-time AI in Dropbox Dash Building the future: highlights from Dropbox’s 2025 summer intern class Fighting the forces of clock skew when syncing password payloads Making camera uploads for Android faster and more reliable How Dropbox Replay keeps everyone in sync Why we built a custom Rust library for Capture Detecting memory leaks in Android applications How we sped up Dropbox Android app startup by 30% Why we chose Apache Superset as our data exploration platform Revamping the Android testing pipeline at Dropbox Our counterintuitive fix for Android path normalization JQuery to React: How we rewrote the HelloSign Editor How we ensure credible analytics on Dropbox mobile apps Engineering Dropbox Transfer: Making simple even simpler Speeding up a Git monorepo at Dropbox with <200 lines of code Building for reliability at HelloSign
Introducing Focus, a new open source Gradle plugin
Ryan Harter · 2022-03-29 · via Dropbox Tech Blog

Working with large projects in Gradle can be a pain. As a project grows larger over time, configuration and build times tend to grow with it. Breaking up large projects into lots of modules can help mitigate this, but unless you meticulously follow the seemingly ever-changing advice about project structure and build script configuration, it’s easy to end up with a build system that’s doing far more than it needs to. And while Gradle is doing that extra work, you have to sit and wait.

At Dropbox, like many companies, we use Gradle to build our Android apps. Our monorepo contains almost 600 projects, most of which aren’t used by our engineers on a day-to-day basis. This means that Android Studio has to load many more modules than required, and we waste time waiting for our tools.

When we’re working on features within large projects, our time is generally spent within a specific feature module. By using reasonable abstractions within our modules we are able to develop code and write tests without depending on most of the other projects in our monorepo. But wouldn’t it be great if we could easily tell Gradle exactly where we’re working and what dependencies we need, and have it ignore the rest?

To answer that question, we recently set out to find an easy way for our engineers to specify the module in which they’re working, and let Gradle and Android Studio ignore the rest.   

The manual way

In the past, we’ve been able to trim the number of projects that Gradle loads by manually adjusting the projects that are included in the settings.gradle.kts file. The quick and dirty way is to simply comment out the projects that you don’t need:

include(":sample:app1")
//include(":sample:app2")
include(":sample:lib1a")
include(":sample:lib1b")
//include(":sample:lib2a")
//include(":sample:lib2b")
//include(":sample:lib2c")
include(":sample:lib-shared")

include(":sample:moved")
project(":sample:moved").projectDir = File("sample/lib-moved")

However, this approach quickly becomes unwieldy as a project grows, and the interdependency of your modules grows more complex.

Another approach is to extract all of those include("...") statements into a separate file. This way, you can more easily switch between modules by simply choosing which files you want to apply:

//apply(from = File("project-1.settings.gradle.kts")
//apply(from = File("project-2.settings.gradle.kts")
//apply(from = File("project-3.settings.gradle.kts")
apply(from = File("settings-all.gradle.kts"))

But with all of the possible permutations, it’s easy for this to become unmanageable too. On top of that, making sure those extra settings files stay in sync can be a nightmare.

Since Gradle already knows about the dependency tree of your project, wouldn’t it be great if there was an easy way to have Gradle create these files for you?

Enter Focus

Focus allows you to do just that. Our Focus Gradle Plugin evaluates your project configuration and creates a unique settings.gradle file for the module you want to focus on. This file only includes the project dependencies that a specific module requires, allowing you to easily ignore the rest. The plugin also creates a .focus file which identifies which module should currently be focused. 

With these files in place, Gradle will only configure the modules you need when you sync your project. Deleting the .focus file—which can be done manually, or by using the clearFocus task—will revert to including all of your modules.

For example, while working on design systems at Dropbox, the Focus plugin allows us to easily focus on our UI Components Playground project (a sample app in our monorepo for working on design system components). This reduces the IDE sync time from 1 minute to 15 seconds. 

To start, we simply run the following command:

./gradlew :applications:uicomponents_playground:focus

This creates a focus.settings.gradle file in the module’s build directory—which only includes the projects that are required to build the uicomponents_playground module—and a .focus file at the root of the project which points to the newly created Gradle settings file. Clicking the Sync Elephant icon in Android Studio will only load the required modules, improving the performance of both the IDE and Gradle.

If we want to spend some time in a different module—perhaps a dependency that needs updating—we can simply focus on that other module and sync.

./gradlew :dsys:components:focus

This way, we’re easily able to trim the number of modules loaded in the IDE and configured by Gradle. When we’re ready to go back to building the entire project, we can simply remove the .focus file in the root directory of the project, or run the following Gradle task:

./gradlew clearFocus

With Focus, our engineers are able to iterate faster throughout the day, allowing them to spend more time focused on the quality of the code they’re developing, and less time waiting for their tools to sync.

Adding the Focus plugin

Focus is a settings plugin, so it should be applied in your settings.gradle(.kts) file. Since we currently publish the plugin to Maven Central, you’ll have to add that as a repository in your pluginsManagement block:


// settings.gradle(.kts)
pluginsManagement {
  repositories {
    mavenCentral()
    gradlePluginPortal()
  }
}

plugins {
  id("com.dropbox.focus") version "0.4.0"
}

Next, move all of your include statements into a settings-all.gradle file:


// settings-all.gradle(.kts)
include ':sample:app2'
include ':sample:lib2c'
include ':sample:lib-shared'
// ...

include ':sample:moved'
project(':sample:moved').projectDir = new File("sample/lib-moved")

Optionally, you can configure the plugin within your settings.gradle.kts file:


// settings.gradle(.kts)
focus {
  // The name of the settings file
  allSettingsFileName = "settings-all.gradle" // Default
  // The name of the pointer file that tells Focus which module to focus on
  // This should be added to your .gitignore file.
  focusFileName = ".focus"  // Default
}

Lastly, don’t forget to add your Focus file—.focus by default—to your .gitignore file.

Once those steps are complete, you can use the focus Gradle tasks in each subproject to reduce the amount of time you have to sit around and wait.

Open source at Dropbox

Dropboxers who have been using the Focus plugin are incredibly happy with the results. Our Android engineers have seen significant improvements in Android Studio and Gradle performance—in some cases reducing the time it takes Android Studio to sync with Gradle from 2 minutes down to 20 seconds. We’re excited by these improvements in our build performance, and are even more excited to share these improvements with the Open Source community.

On a personal note: I’ve been involved with the Open Source community for many years, both developing and contributing to open source libraries. When I was considering where I wanted to work next, it was important to me to be a part of a company that not only values but actively participates in the open source community. One of the things that excited me about Dropbox was learning how much the company supports the use of open source libraries and also encourages Dropboxers to contribute back to the community. (If that’s something that excites you too, we’re hiring!)

When I prototyped Focus and proposed that it could become a general use plugin, Dropbox encouraged me to take the time to build it and share it with the community. While Focus isn’t quite ready for 1.0, we invite you to give it a try and let us know how it works for you. And if you run into any issues, please let us know.