π οΈ How to Use Composite Builds in Gradle to Work Locally with Shared Modules π€
In modern projects that use Gradle β whether Android, KMP, Kotlin backend, or others β itβs common to centralize business logic in an external library π published to remote repositories like GitHub Packages or Maven Central . However, during development, constantly publishing changes can be slow and inefficient.
An ideal alternative β is to use Gradleβs Composite Builds. This allows you to reference the source code of an external project directly, without the need to publish it βοΈ.
In this article π, youβll learn how to configure βοΈ an Android project (used as an example) to locally include a shared module using Composite Builds π. That way, youβll replace a published dependency with the moduleβs actual source code π.
π Project Structure
Letβs assume your folder structure looks like this:
/my-project/ π
βββ test-android/ # Main Android project π±
βββ sharedlibrary/ # Shared project (KMP or Android Library) π§©
β βββ shared-library/ # Shared module π§
π Create Android Library or KMP project (For this example)
Letβs create an Android library project from scratch, which will serve as a practical example to demonstrate how to integrate local libraries into Android projects. This project will contain a string constant that we will use in a small Android application project.
Within the shared-library module, add the following code:
object TestData {
const val value = "External value from shared-library"
}
π±Create Android Project
Now, we proceed to create a simple Android project (Empty Activity) where we will add the following configurations:
Within the build.gradle.kts of the :app module, add a reference dependency:
implementation("com.example.shared:shared-library")
βοΈ Configure settings.gradle.kts
in the Android Project
The next step is to configure the settings.gradle.kts
file to integrate our local library. Add the following includeBuild
block to reference the sharedlibrary
project and define a dependency substitution:
includeBuild("../sharedlibrary") {
dependencySubstitution {
substitute(module("com.example.shared:shared-library"))
.using(project(":shared-library"))
}
}
π§ What does this block do?
includeBuild("../sharedlibrary")
: Tells Gradle to include the externalsharedlibrary
project as a Composite Build.dependencySubstitution
: Defines a rule to substitute the published dependency (com.example.shared:shared-library
) with the local module:shared-library
.
Make sure the module name (:shared-library
) exactly matches the one declared in the settings.gradle.kts
file of the sharedlibrary
project.
β Runtime Verification
To confirm that our Android project is indeed using the local version of the shared library, we referenced the TestData
object that was previously defined in the shared-library
.
In MainActivity
, we simply used it like this:
Text(
text = TestData.value
)
When running the app, the following message is displayed:
External value from shared-library
This confirms that the local changes in the library are being picked up by the Android project, proving that the Gradle Composite Build setup is working as expected.
From now on, any changes you make in shared-library
will be instantly visible in test-android
without the need to publish the library.
Another way to confirm that the settings have been applied correctly is by checking the Project View tool in Android Studio (or IntelliJ IDEA), where the sharedlibrary
project should now appear as a referenced module.
π Important Note About Local Changes
Android Studio may show changes from the shared library (e.g., Android Chart Library) as if they were part of your main Android project, even though they belong to a separate module. This can create confusion, especially when reviewing changes before committing.
To get a more accurate and project-specific view of what has really changed, you can use Git in the terminal:
β View all modified or untracked files
git ls-files -m -o --exclude-standard
π Count how many files have changes
git ls-files -m -o --exclude-standard | wc -l
π‘ Run these commands inside each project (test-android
and sharedlibrary
) to see exactly which files were modified. This will help you avoid the misleading perception of changes caused by how Android Studio aggregates changes across builds.
π‘ Extra Tip: Use a Git Tool Outside of Android Studio
When working with Composite Builds, tools like GitKraken, Fork, Sourcetree, or even the terminal are better alternatives for reviewing and managing Git changes. These tools offer a clearer, project-specific diff view, avoiding the false positives that can appear in the built-in VCS (Version Control System) of Android Studio or IntelliJ IDEA.
This ensures that only actual changes from your current working directory are committed β not the ones mistakenly shown as modified due to how composite builds are linked in the IDE.
π§ Conclusion
Using Composite Builds with dependency substitution boosts your development workflow β‘. It allows you to work with local changes in shared modules instantly, without needing to publish intermediate versions π¦. This speeds up iteration and testing across multiple modules in real-time, improving productivity π.
For clearer change tracking, consider using Git tools like GitKraken, Fork, Sourcetree, or even the terminal π» to avoid confusion caused by IDE-specific Git handling.
In short, Composite Builds in Gradle are a powerful and efficient solution for managing shared dependencies in modern projects.