A Comprehensive Guide to Modularization for Large-Scale Android Projects

Daniel AlomeDaniel Alome
5 min read

In the ever-evolving world of Android development, developers often focus on the latest trends like Jetpack Compose, Kotlin Coroutines, and dependency injection with Hilt. However, there’s one critical yet under-discussed topic in Android development—modularization. Modularization involves breaking down your app into smaller, self-contained modules that handle specific functionality. This practice is invaluable for scaling an app, improving maintainability, and reducing build times. In this guide, we'll dive into the concept of modularization and why it's essential. We will also walk through a practical example of modularizing an Android app.

Why Modularization?

The benefits of modularization are significant, especially for large applications. Let’s consider the advantages:

  1. Faster Build Times: Instead of rebuilding the entire project for each change, only the affected module needs to be rebuilt.

  2. Improved Scalability: Modularization helps avoid a monolithic codebase, making it easier to scale as your app grows.

  3. Parallel Development: Developers can work on separate modules simultaneously, reducing friction and enhancing collaboration.

  4. Code Reusability: Modules can be shared across apps or features.

  5. Testability: Isolated modules are easier to test.

However, despite these benefits, modularization is often overlooked, mainly due to the upfront investment required to refactor existing code or build from scratch. The following steps demonstrate how to modularize your app effectively with a practical project example.

Step 1: Set Up the Project

Let’s create a simple Android project with three modules to demonstrate modularization:

  • App Module: The entry point of the app.

  • Core Module: Shared utilities (e.g., logging, network operations).

  • Feature Module: A specific feature of the app (e.g., a user profile screen).

Creating the Project:

  1. Open Android Studio and create a new project with an Empty Activity template.

  2. Name the project ModularAppExample or anything.

  3. Set the language to Kotlin.

Step 2: Add Modules to the Project

We will add two new modules to the project: core and feature_user_profile.

  1. Right-click on the project root directory and select New > Module.

  2. Choose Android Library and name the first module core.

  3. Repeat for the second module and name it feature_user_profile.

Your project structure will look like this:

ModularAppExample/
    app/
    core/
    feature_user_profile/

Step 3: Configure Dependencies

To allow the modules to interact, you need to configure their dependencies in Gradle.

1. Update settings.gradle:

groovyCopy codeinclude ':app', ':core', ':feature_user_profile'

2. Update app/build.gradle:

kotlinCopy codedependencies {
    implementation project(":core")
    implementation project(":feature_user_profile")
}

3. Update core/build.gradle:

The core module will contain shared utilities (like logging). For now, we don’t need any special dependencies here.

kotlinCopy codedependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
}

4. Update feature_user_profile/build.gradle:

The feature_user_profile module will rely on the core module for shared functionality.

kotlinCopy codedependencies {
    implementation project(":core")
    implementation "androidx.appcompat:appcompat:1.3.1"
}

Step 4: Implement Code in Each Module

Now, let’s implement the functionality in each module.

Core Module (core)

In the core module, we’ll create a simple utility for logging.

  1. Create a new Kotlin class Logger.kt in the core/src/main/java/com/example/core/ directory:
package com.example.core

object Logger {
    fun log(message: String) {
        println("Log: $message")
    }
}

Feature Module (feature_user_profile)

In the feature_user_profile module, we will create a UserProfileActivity that will be launched by the app module.

Create a new activity, UserProfileActivity.kt, in the feature_user_profile/src/main/java/com/example/feature_user_profile/ directory:

package com.example.feature_user_profile

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.example.core.Logger

class UserProfileActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            UserProfileScreen()
        }

        // Log a message using the Logger class from the core module
        Logger.log("UserProfileActivity is created!")
    }
}

@Composable
fun UserProfileScreen() {
    // Scaffold is a useful layout for Material Design components
    Scaffold(
        topBar = {
            TopAppBar(
                title = { Text("User Profile") }
            )
        }
    ) { paddingValues ->
        Box(
            modifier = Modifier
                .fillMaxSize()
                .padding(paddingValues),
            contentAlignment = Alignment.Center
        ) {
            Column(
                horizontalAlignment = Alignment.CenterHorizontally,
                verticalArrangement = Arrangement.spacedBy(16.dp)
            ) {
                Text(
                    text = "User Profile",
                    fontSize = 24.sp,
                    style = MaterialTheme.typography.titleMedium
                )
                Text(
                    text = "Welcome to the user profile screen!",
                    style = MaterialTheme.typography.bodyMedium
                )
            }
        }
    }
}

App Module (app)

Finally, in the app module, we will launch UserProfileActivity.

Open MainActivity.kt and modify it as follows:

package com.example.modularappexample

import android.content.Intent
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.*
import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.example.feature_user_profile.UserProfileActivity

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MainScreen(onNavigateToProfile = {
                // Launch UserProfileActivity
                val intent = Intent(this, UserProfileActivity::class.java)
                startActivity(intent)
            })
        }
    }
}

@Composable
fun MainScreen(onNavigateToProfile: () -> Unit) {
    // Define the UI for the main screen
    Box(
        modifier = Modifier
            .fillMaxSize()
            .padding(16.dp),
        contentAlignment = Alignment.Center
    ) {
        Column(
            horizontalAlignment = Alignment.CenterHorizontally,
            verticalArrangement = Arrangement.spacedBy(16.dp)
        ) {
            Text(
                text = "Welcome to ModularAppExample!",
                style = MaterialTheme.typography.titleMedium
            )

            Button(onClick = onNavigateToProfile) {
                Text("Go to User Profile")
            }
        }
    }
}

Add the following entry to the AndroidManifest.xml in the app module to register UserProfileActivity

<activity android:name="com.example.feature_user_profile.UserProfileActivity" />

Step 5: Build and Run the Project

Now that you’ve set up the modules and their interactions, you can build and run the project. The MainActivity in the app module will launch the UserProfileActivity from the feature_user_profile module and it will print a log message using the Logger class from the core module.

Final Project Structure

After completing these steps, your project will look like this:

ModularAppExample/
    app/
        src/
            main/
                AndroidManifest.xml
                java/
                    com/example/modularappexample/
                        MainActivity.kt
    core/
        src/
            main/
                java/
                    com/example/core/
                        Logger.kt
    feature_user_profile/
        src/
            main/
                AndroidManifest.xml
                java/
                    com/example/feature_user_profile/
                        UserProfileActivity.kt

Sub note: Naming Feature Modules Effectively

When naming modules in a modularized Android project, clarity and consistency are crucial. For instance, feature_user_profile is a great name because it is descriptive, indicating that this module handles functionality related to the user profile. The feature_ prefix provides scope, distinguishing it from core or shared modules, and the snake_case format enhances readability.

If your app has multiple related features, consider broader namespaces like feature_profile or even hierarchical names like feature_user/profile if supported by your build system. Remember, a well-organized module structure makes your codebase scalable and easy to navigate!

Conclusion

By following the steps above, you’ve modularized an Android app into separate modules: core, feature_user_profile, and app. This architecture not only makes your app easier to maintain but also improves scalability, reduces build times, and increases parallel development efficiency. As your app grows, you can further refine and expand your modularization strategy to include additional features, dynamic modules, and more sophisticated dependency management.

Modularization may take some upfront effort, but the benefits it provides—faster builds, better maintainability, and cleaner code—make it a powerful tool for Android development, especially as your projects scale.

10
Subscribe to my newsletter

Read articles from Daniel Alome directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Daniel Alome
Daniel Alome

Software Engineer | IoT lover | Romans 8:38