Mastering Gradle Dependency Resolution for Android Projects with Multiple Maven Repositories

Mustafa Yiğit
Papara Tech
Published in
4 min readAug 21, 2024

As an Android developer, managing Gradle dependencies is a crucial part of your workflow. When your project depends on multiple Maven repositories, ensuring the correct dependencies are used can sometimes feel like a juggling act. If you’ve ever faced issues with Gradle pulling the wrong version or found yourself wondering why a specific dependency wasn’t resolved as expected, you’re not alone. Let’s explore how to manage Gradle’s dependency resolution effectively, ensuring your Android project pulls in the right dependencies from the right places.

Understanding Gradle’s Dependency Resolution

Think of Gradle as a meticulous librarian organizing books — in this case, your dependencies. When you define dependencies in your gradle file, Gradle looks through the repositories you've listed, in the order you've specified. If it finds what it’s looking for in the first repository, it stops and uses that version.

This behavior can sometimes lead to unexpected results, especially when repositories contain overlapping versions of the same library. Understanding and controlling this process is key to ensuring your Android app uses the versions you intend.

Setting Up Multiple Maven Repositories

Let’s start with a common scenario where your Android project relies on several Maven repositories:

repositories {
maven(url = "https://jitpack.io") // For GitHub projects
maven(url = "https://dl.bintray.com/android/android-tools") // For specific Android tools
google() // Google's Maven repository for Android libraries
mavenCentral() // Maven Central for a wide range of libraries
}

In this configuration:

  • Gradle checks JitPack first, ideal for integrating GitHub-hosted libraries.
  • Next, it checks Bintray’s repository, often used for specialized Android tools.
  • Then, it looks in Google’s repository for Android-specific libraries.
  • Finally, it checks Maven Central for other dependencies.

If a dependency exists in JitPack, that’s what Gradle will use, even if a newer version is available in Maven Central.

Controlling Repository Usage with exclusiveContent

If you need precise control over where specific dependencies are resolved from, Gradle’s exclusiveContent block is your best friend. This feature allows you to specify that certain dependencies should only come from a particular repository.

Here’s an example tailored for Android developers:

repositories {
maven(url = "https://jitpack.io")
maven(url = "https://dl.bintray.com/android/android-tools")
google()
mavenCentral()

exclusiveContent {
forRepository {
maven {
url = java.net.URI.create("https://oss.sonatype.org/content/repositories/snapshots")
}
}
filter {
includeGroup("com.company.lib") // Specific library
}
}
}

In this setup:

  • Gradle will only fetch dependencies from the com.company.lib group from the Sonatype snapshot repository.
  • This ensures that if you’re using this library you’ll always get the latest snapshot versions from Sonatype, even if other repositories have different versions.

Leveraging Repository Content Filtering

Gradle’s repository content filtering is an advanced feature that helps you manage which modules are pulled from which repositories. This is particularly useful for Android projects that depend on multiple repositories with overlapping modules.

For instance, you might want to limit the scope of dependencies from certain repositories to avoid conflicts:

repositories {
maven(url = "https://repo1.maven.org/maven2/") {
content {
includeGroupByRegex("com\\.android.*")
excludeModule("com.android.support", "support-v4")
}
}
maven(url = "https://jitpack.io") {
content {
includeGroup("com.github.user")
}
}
}

In this configuration:

  • Maven Central (https://repo1.maven.org/maven2/) will only provide dependencies from the com.android.* group and exclude the com.android.support:support-v4 module.
  • JitPack will only serve dependencies from the com.github.User group.

This approach helps manage dependency conflicts and ensures that your project uses the correct versions and modules.

Practical Gradle Techniques for Android Developers

Beyond exclusiveContent and content filtering, here are additional techniques that can streamline dependency management for Android developers:

1. Include and Exclude Rules

You can fine-tune which versions of dependencies are used by specifying include and exclude rules:

repositories {
google()
mavenCentral()
}

configurations.all {
resolutionStrategy {
eachDependency { DependencyResolveDetails details ->
if (details.requested.group == "com.squareup.okhttp3" && details.requested.name == "okhttp") {
details.useVersion("4.9.2") // Ensures specific version of OkHttp is used
details.because("This version is tested and verified for our app")
}
}
}
}

Here, we lock okhttp to version 4.9.2 to ensure compatibility with our Android app, regardless of what’s available in the repositories.

2. Repository Filtering with content

Control which modules are considered from specific repositories:

repositories {
maven(url = "https://repo1.maven.org/maven2/") {
content {
includeGroupByRegex("com\\.android.*")
excludeModule("com.android.support", "support-v4")
}
}
}

This ensures that only specific groups or modules are fetched from certain repositories, keeping your project’s dependencies clean and free of conflicts.

Best Practices for Android Dependency Management

To keep your Android project’s dependencies in check:

  • Order Matters: Place the most crucial or frequently updated repositories first in your list.
  • Use exclusiveContent and Content Filtering Wisely: These tools help direct dependencies to the right repositories, reducing the risk of version conflicts and ensuring the correct modules are used.
  • Audit Regularly: Periodically review and update your dependencies to prevent security vulnerabilities and maintain compatibility.

Conclusion

Managing dependencies in Android projects with Gradle can sometimes feel complex, especially with multiple Maven repositories in play. By leveraging features like exclusiveContent, repository content filtering, and resolution strategies, you can gain precise control over which dependencies are used and from where they are sourced. This ensures a smoother, more predictable build process, helping you keep your Android project clean, secure, and efficient.

✨ Next time you find yourself wrestling with dependency resolution, remember you can find more details than here on this Gradle page👇

https://docs.gradle.org/current/userguide/declaring_repositories_adv.html#sec:repository-content-filtering

--

--