The development of Android applications is rarely complete without the use of additional modules. They allow you to structure your project, reuse code, and integrate third-party libraries. However, the process of connecting a module to Android Studio often raises questions for beginners - and even experienced developers are faced with nuances when working with multi-module projects or non-standard dependencies.
In this article we will analyze all the stages: from creating a new module to solving common errors assemblies. You will learn how to properly configure build.gradle, avoid dependency conflicts and optimize the project structure. And for those who work with legacy code, there is a separate section on connecting outdated modules without failures.
The material is relevant for Android Studio Giraffe (2022.3.1) and newer, but the basic principles also apply to earlier versions. If you use Gradle 8.x or AGP (Android Gradle Plugin) 8.0+, pay attention to the changes in the configuration syntax - we have noted them separately.
1. What is a module in Android Studio and why do you need it
Module in context Android Studio is an independent unit of code that can contain:
- ๐ฆ Source code (
.java/.ktfiles) - ๐ Resources (layouts, strings, images)
- ๐ฆ Dependencies (libraries and other modules)
- ๐ Configuration files (
AndroidManifest.xml,build.gradle)
Main scenarios for using modules:
- ๐ Separation of functionality: moving authorization, payments or analytics into separate modules for reuse in different projects.
- ๐ ๏ธ Testing: isolating components for unit tests.
- ๐ฑ Dynamic Feature Modules: loading parts of the application on demand (for example, to save space).
- ๐ SDK Integration: connecting ready-made solutions (for example, Firebase or Google Maps).
Without a modular architecture, large projects become unmanageable: build times increase, the risk of dependency conflicts increases, and refactoring becomes a nightmare. For example, in a monolithic application, changing one screen may require rebuilding the entire project, whereas with modules it is enough to build only the changed component.
2. Preparing the project for adding a module
Before connecting a new module, make sure that your project is ready for changes. Neglecting this step often leads to synchronization errors Gradle or version conflicts.
Update Android Studio to the latest version
Synchronize the current project with Gradle (the "Sync Now" button)
Check the free disk space (minimum 2 GB)
Commit the current changes to the version control system-->
Pay special attention to the file settings.gradle (or settings.gradle.kts for Kotlin DSL). This is where all project modules are registered. Example of a basic structure:
pluginManagement {
repositories {
gradlePluginPortal()
google()
mavenCentral()
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
}
}
rootProject.name = "MyApplication"
include ':app' // Main application module
If you are working with multi-module project, this file also specifies the paths to additional modules. For example, for a module features/auth you will need to add the line:
include ':features:auth'
project(':features:auth').projectDir = file('features/auth')
โ ๏ธ Attention: The syntax for Gradle 8.x the syntax forsettings.gradle.ktshas changed. If you use Kotlin DSL, replaceincludewithincludeBuildto connect composite assemblies (composite builds).
3. Creating a new module: step-by-step guide
You can add a module to the project in two ways: through the interface Android Studio or manually. The first method is easier for for beginners, the second gives more control over the structure.
Method 1: Through the GUI Android Studio
- Open the menu
File โ New โ New Module. - Select the module type:
- ๐ฑ Phone & Tablet Module โ for standard feature modules.
- ๐ Android Library โ if the module will be used as a dependency in others projects.
- ๐ Dynamic Feature Module โfor on-demand content.
auth or payment-sdkAvoid spaces and special characters.minimum SDK (must be the same as the main module or higher).Finish and wait for synchronization Gradle.Method 2: Manual creation
If you need a non-standard structure (for example, nested modules features/login), perform the following steps:
- Create a folder for the module in the root of the project (for example,
features/auth). - Inside the folder, create a file
build.gradlewith the following content:plugins {id 'com.android.library'
id 'org.jetbrains.kotlin.android'
}
android {
namespace 'com.yourpackage.auth'
compileSdk 34
defaultConfig {
minSdk 24
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
}
dependencies {
// Basic dependencies
implementation 'androidx.core:core-ktx:1.12.0'
testImplementation 'junit:junit:4.13.2'
} - Add module to
settings.gradle(as shown in the previous section). - Synchronize the project with Gradle.
- ๐ Module
mylibraryat the root โimplementation project(':mylibrary') - ๐ Module
features/paymentโimplementation project(':features:payment') - ๐ Move the common code into a third module (for example,
:core). - ๐ฆ Use interfaces instead of direct dependencies.
- ๐ Apply Dependency Inversion Principle (DIP) from SOLID.
If you are creating a module for reuse in several projects, immediately set up publishing to a local or remote Mavenrepository. This will save time when integrating into other applications.
4. Connecting a module to the main project
After creating a module, it needs to be connected as a dependency to the main application module (:app) or other modules. This is done through the file build.gradle (Module: app).
Basic connection
Open build.gradle the main module and add a line to the block dependencies:
dependencies {implementation project(':features:auth') // For the features/auth module
// Other dependencies...
}
If the module is located in the same directory as the main project, the path is relative to the root. For example:
Configuration of dependencies between modules
When working with several modules, it is important avoid cyclic dependencies (when module A depends on B, and B on A). This results in build errors. For example:
// โ Error: circular dependency
// The :auth module depends on :app, and :app depends on :auth
Solutions for avoiding cyclic dependencies dependencies:
| Dependency type | Syntax | When to use |
|---|---|---|
implementation |
implementation project(':mymodule') |
For dependencies needed only at compile time. |
api |
api project(':mymodule') |
If a module exposes APIs to other modules. |
compileOnly |
compileOnly project(':mymodule') |
For dependencies needed only at compile time (not included in the APK). |
runtimeOnly |
runtimeOnly project(':mymodule') |
For dependencies needed only at runtime. |
โ ๏ธ Attention: Useapiinstead ofimplementationincreases build time, since Gradle it must analyze transitive dependencies. Useapionly when necessary (for example, for libraries that must be accessible to other modules).
5. Solving common errors when connecting a module
Even experienced developers encounter problems when working with modules. Let's look at the most common errors and how to fix them.
Error 1: "Module not specified"
Cause: The module was not added to settings.gradle or the wrong path was specified.
Solution:
- Check that there is a string in
settings.gradleMake sure that the path ininclude ':mymodule'. - Make sure the path is in
project(':mymodule').projectDiris correct. - Synchronize the project with Gradle (
File โ Sync Project with Gradle Files).
Error 2: Dependency version conflict
Situation: Two modules use different versions of the same library (for example, androidx.appcompat:appcompat:1.6.1 and 1.4.2).
Solution:
- ๐ Use the command
./gradlew :app:dependenciesto analyze dependencies. - ๐ Add resolution strategy to
build.gradlethe main module:configurations.all {resolutionStrategy {
force 'androidx.appcompat:appcompat:1.6.1' // Force installation of the version
}
} - ๐ Update dependencies in conflicting modules up to the same version.
Error 3: "Duplicate class" during assembly
Cause: Two modules include the same library with different obfuscation rules (ProGuard/R8).
Solution:
- Check rules
proguard-rules.proin each module. - Exclude duplicate classes through
packagingOptionsinbuild.gradle:android {packagingOptions {
exclude 'META-INF/DEPENDENCIES'
exclude 'META-INF/LICENSE'
}
}
What to do if Gradle freezes during synchronization?
If Gradle thinks for a long time during synchronization after adding a module:
1. Close Android Studio and delete the folder .gradle in the user's home directory.
2. Delete the folder build in the project root.
3. Restart Android Studio and try to synchronize the project again.
4. If the problem persists, check the file gradle-wrapper.properties - it may be that an outdated version is being used Gradle (current at the time of writing: 8.4).
6. Working with Dynamic Feature Modules
Dynamic Feature Modules (DFM) is a special type of modules that are downloaded to the userโs device on demand. They allow reduce the size of the initial APK/AAB and load additional functionality only when needed (for example, premium features or rarely used screens).
Creating DFM
- Select
File โ New โ New Module โ Dynamic Feature Module. - Specify the module name (for example,
premium_features). - B
build.gradleAdd module:android {dynamicFeatures = [':premium_features'] // Indicate that this is DFM
} - Configure loading in the code:
val request = SplitInstallRequest.newBuilder().addModule("premium_features")
.build()
SplitInstallManager.startInstall(request)
.addOnSuccessListener { / Module loaded / }
.addOnFailureListener { / Error downloads / }
DFM limits
- ๐ฑ Minimum Android version: 5.0 (API 21).
- ๐ฆ The module cannot exceed 10 MB (for a compressed file).
- ๐ You cannot use DFM for basic functionality (for example, authorization).
- ๐ก Requires an Internet connection to download.
DFM are especially useful for:
- ๐ฎ Game levels or additional content.
- ๐ Premium features (for example, advanced filters in a photo application).
- ๐ Localizations for rarely used languages.
โ ๏ธ Attention: When using DFM, test the behavior of the application on slow connections (2G/3G). Users can interrupt the loading of the module, which will lead to a crash if this case is not handled in the code.
7. Optimizing the assembly of multi-module projects
The more modules in the project, the longer the assembly. Fortunately, there are ways to speed up the process:
1. Parallel assembly of modules
Add to gradle.properties:
org.gradle.parallel=true
org.gradle.caching=true
This will allow Gradle to assemble independent modules in parallel and cache the results.
2. Incremental annotation processing
For projects with Dagger, Room or DataBinding add to build.gradle:
kapt {useBuildCache = true
}
3. Separation into layers (Layered Architecture)
An example of a structure to speed up assembly:
- ๐ฆ
:coreโ general logic (without dependencies on Android). - ๐ฑ
:featureโ UI components (depending on:core). - ๐
:dataโworking with the network and database.
This structure allows you to collect only changed layers.
| Problem | Solution | Saving time |
|---|---|---|
| Long first assembly | Use offline mode in Gradle |
up to 30% |
| Slow tests | Split unit and UI tests into modules | up 50% |
| Dependency conflicts | Centralized version control in buildSrc |
up to 20% |
Use ./gradlew --profile to generate a build time report This will help identify. "bottlenecks" in your project.
8. Integration of third-party modules (SDK, libraries)
In addition to our own modules, it is often necessary to connect third-party solutions - for example, Firebase, Google Mobile Ads or Facebook SDK. The process is different from connecting local modules.
Connecting via Maven
Most libraries are distributed via Maven Central or Google Maven Repository. It is enough to add a dependency to build.gradle:
dependencies {implementation 'com.google.firebase:firebase-analytics:21.5.0'
implementation 'com.facebook.android:facebook-android-sdk:16.1.2'
}
Connecting a local AAR file
If you have a .aarfile (for example, a closed SDK from partners), place it in the libs module folder and add:
dependencies {implementation files('libs/mysdk-release.aar')
}
Connection via JitPack
For libraries hosted on GitHub, it is often used JitPack. Add the repository to settings.gradle:
dependencyResolutionManagement {repositories {
maven { url 'https://jitpack.io' }
}
}
Then connect the library:
dependencies {implementation 'com.github.User:Repository:Tag'
}
โ ๏ธ Attention: When connecting the SDK via.aarmake sure that the file is signed and compatible with your version. Android Gradle Plugin. Some AAR files require additional rules. use ProGuard orconsumerProguardFilesVbuild.gradle.
FAQ: Frequently asked questions about connecting modules
Is it possible to connect a module from another project?
Yes, use it for this composite builds. In settings.gradle the main project add:
includeBuild('../path-to-other-project')
Then connect the module as usual: implementation project(':other-module').
How to make the module available to other developers?
Publish it to Maven Central or a private repository:
- Configure the plugin
maven-publishinbuild.gradlemodule. - Generate a GPG key for signing artifacts.
- Load the module using
./gradlew publish.
For local testing, you can use mavenLocal().
Why is the main project not updated when a module is changed?
The problem is most often related to caching Gradle. Try:
- Run
Build โ Clean Project. - Delete folder
buildin the project root. - Disable cache:
File โ Settings โ Build, Execution, Deployment โ Gradle โ Uncheck "Offline work".
How to check which modules my project uses?
Run the command in the terminal:
./gradlew :app:dependencies --configuration implementation
You can use a plugin to visualize dependencies gradle-dependency-graph-generator.
Is it possible to connect a Kotlin module to a Java project?
Yes, Kotlin is fully compatible with Java. Make sure that:
- The plugin is used in the module
kotlin-android. - The version Kotlin matches in both modules (or is compatible).
- A repository has been added to the main project
build.gradlemain project repository added Kotlin:
buildscript {repositories {
mavenCentral()
}
}