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/.kt files)
  • ๐Ÿ“„ 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.

๐Ÿ“Š What type of modules do you use? more often?
Feature modules
Library modules
Dynamic Feature Modules
I donโ€™t use modules

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 for settings.gradle.ktshas changed. If you use Kotlin DSL, replace include with includeBuild to 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

  1. Open the menu File โ†’ New โ†’ New Module.
  2. 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.
  • Specify the module name (for example, auth or payment-sdkAvoid spaces and special characters.
  • Customize minimum SDK (must be the same as the main module or higher).
  • Click 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:

    1. Create a folder for the module in the root of the project (for example, features/auth).
    2. Inside the folder, create a file build.gradle with 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'

      }

    3. Add module to settings.gradle (as shown in the previous section).
    4. Synchronize the project with Gradle.
    5. ๐Ÿ’ก

      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:

      • ๐Ÿ“ Module mylibrary at the root โ†’ implementation project(':mylibrary')
      • ๐Ÿ“ Module features/payment โ†’ implementation project(':features:payment')

      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:

      • ๐Ÿ”„ Move the common code into a third module (for example, :core).
      • ๐Ÿ“ฆ Use interfaces instead of direct dependencies.
      • ๐Ÿ”— Apply Dependency Inversion Principle (DIP) from SOLID.
    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: Use api instead of implementation increases build time, since Gradle it must analyze transitive dependencies. Use api only 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:

    1. Check that there is a string in settings.gradle Make sure that the path in include ':mymodule'.
    2. Make sure the path is in project(':mymodule').projectDir is correct.
    3. 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:dependencies to analyze dependencies.
    • ๐Ÿ“ Add resolution strategy to build.gradle the 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.pro in each module.
    • Exclude duplicate classes through packagingOptions in build.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

    1. Select File โ†’ New โ†’ New Module โ†’ Dynamic Feature Module.
    2. Specify the module name (for example, premium_features).
    3. B build.gradle Add module:
      android {
      

      dynamicFeatures = [':premium_features'] // Indicate that this is DFM

      }

    4. 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 .aar make sure that the file is signed and compatible with your version. Android Gradle Plugin. Some AAR files require additional rules. use ProGuard or consumerProguardFiles V build.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:

    1. Configure the plugin maven-publish in build.gradle module.
    2. Generate a GPG key for signing artifacts.
    3. 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:

    1. Run Build โ†’ Clean Project.
    2. Delete folder build in the project root.
    3. 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.gradle main project repository added Kotlin:
    buildscript {
    

    repositories {

    mavenCentral()

    }

    }