Android App Modularization: 4 Useful Tips to Start

Share This Post

Android app modularization refers to the process of breaking down an Android app into smaller, independent modules. These modules can be thought of as building blocks that can be combined to form the complete app.

Each module is typically responsible for a specific feature or functionality of the app and can be developed, tested, and deployed independently. Modularizing an app can provide several benefits, including easier maintenance, faster development cycles, improved scalability, and enhanced performance.

By isolating changes to specific modules, developers can make updates more efficiently and avoid unintended side effects. Furthermore, modularization can help reduce the app’s size and improve its overall performance by allowing users to download only the necessary components. In summary, modularization is a powerful technique that can help developers create more efficient, scalable, and maintainable Android apps.

Context

We work with a legacy project whose development started more than 4 years ago by another company with other standards, practices, and experiences.

In this project we work with 2 weeks sprints, always delivering new features and fixing bugs, with very closed scopes where adding technical tasks is difficult. We have been adding common best practices like testing and clean architecture, and we have also added new elements within the Android development environment like Room or Jetpack Compose.

Now we start to face a need: we need to focus on Android app modularization to get its advantages.

Android App Modularization: Why do it?

Android App Modularization can bring several benefits to your application’s maintenance, scalability, and development process.

  • Faster builds. Gradle speeds up the compilation time of your project by allowing you to do tasks in parallel and reuse all the code already compiled without modifying it. When you start a new project you may not appreciate the compilation time, but when your project grows it is common to have compilation times of around 5 minutes. To adjust small details of the view, you must compile your project between 3 and 5 times, thus accumulating dead time.
  • It tends to simplify development. Working in small modules focused on a single functionality makes it easier to correct, maintain and improve the code. Just by limiting the size of the code, reviewing or adding tests to start a refactor becomes more comfortable.
  • Reuse modules between apps. Perhaps one of your modules can easily become a library and you can open it to the open-source community with all the benefits without affecting your entire app, both in terms of security and integration.
  • It makes it easier to add more people to the team. By diving your code into smaller modules, you can assign people or teams to them directly without affecting the rest of the app. For example, if you have a chat feature in your Android app, and you need help to reach a deadline with a functionality, just having to understand that module without having to understand the rest of the app makes it much faster for you to receive or give help.
  • Improved test automation. By being able to test modules without having to do the whole flow, tests will go faster and errors from previous steps are not propagated. You can create specific rules in your CI to speed up your compilation time, for example only activate UI tests, usually the slowest ones, when merging to your main development branch.
  MotionLayout - Create Animations in your Android app

Android App Modularization: Tips to Start

How can we start our Android app modularization without stopping development or affecting our ability to deliver?

When facing such a big challenge we tend to get overwhelmed, but the most important advice is to have patience. In the end, we will find the first step of the way and gradually we will be adding modules until we have it completely modularized.

In this article, we are not looking for a perfect recipe for how to modularize your app. Every project is different and approaching it with strict steps can be more of a problem than a solution. For example, if we were told that in our project we will start with the networking part, it would be chaotic and we would fail in the attempt to see that we have some dependencies in analytics and error display. Just to create these two modules that will use networking, we could lose a couple of weeks to have everything tested and make sure we have not broken anything. And back to the context, we don’t have these two weeks to focus on technical tasks. We must adapt to our situation and look for ways to add small modules to be able to address networking and do it in the typical sprint endings with some space for external tasks.

Analyze your dependencies

Look for a small functionality with few dependencies or better yet, none. In our case, it was all the functional programming part and all the Kotlin extensions we have created over the years. We created the module, moved all the code there, tested included, and compiled. Eureka! It worked! Easy? Well, we had to change all the imports of the project, more than 100 modified files, and check that we had not broken any test and that all the functionalities of the app were still intact, but we had our first module.

  Exploring SwiftUI’s ScrollTargetBehavior: Elevating Your UI to the Next Level

Test before your start

A good practice that we should have in our projects would be to have a good test base, but sometimes we get to a project and it is not like that. Therefore, if we find that small functionality without dependencies, before moving it to the new module, we will create some tests that wrap and migrate everything to the new module, and hopefully change imports. We were able to do the first module because we already had a good base of tests that covered this part.

Using Gradle to compose dependencies

One of the drawbacks that we can find when modularizing our project can be the management of dependencies and versions. It is often the typical error of compilation of version conflicts of the Android X library. To do this, we can take advantage of Gradle to compose our build.gradle files. In our project, we have a dependencies.gradle file where we first define the versions of our dependencies and then the dependencies themselves.

 


versions = [
"koin_version"              	: "3.1.4",
]

libs = [
    "koin"     	 : "io.insert-koin:koin-android:$versions.koin_version",
    "koinCore"   	 : "io.insert-koin:koin-core:$versions.koin_version",
    "koinJavaCompat" : "io.insert-koin:koin-android-compat:$versions.koin_version",
    "koinCompose"  : "io.insert-koin:koin-androidx-compose:$versions.koin_version",
]

As you can see when using koin we have several dependencies that share versions, and if we had to update it to solve some error, we only have to come here and not look in all the Gradle files that we have in each module of the project.

But, if we have many modules, we will have many build.gradle files and we can take advantage of the composition to save a lot of repeated code. If we define a file with the basic configuration of all the Android modules, the only thing we will have to add in each file is a line of code and the particular dependencies of that one.

Android_lib.gradle
apply plugin: 'com.android.library'
apply plugin: 'org.jetbrains.kotlin.android'
apply plugin: 'kotlin-kapt'
apply from: "$rootDir/buildsystem/dependencies.gradle"

android {
  compileSdkVersion configs.compileSdk

  defaultConfig {
	targetSdkVersion configs.targetSdk
	minSdkVersion configs.minSdk

	testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
	consumerProguardFiles "consumer-rules.pro"

  }

  buildTypes {

	debug {
  	debuggable = true
	}

	release {
  	minifyEnabled false
  	proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
	}
  }
  compileOptions {
	sourceCompatibility JavaVersion.VERSION_1_8
	targetCompatibility JavaVersion.VERSION_1_8
  }
  kotlinOptions {
	jvmTarget = '1.8'
  }
  sourceSets {
	main.java.srcDirs += 'src/main/kotlin'
	test.java.srcDirs += 'src/test/kotlin'
	androidTest.java.srcDirs += 'src/androidTest/kotlin'
  }
  buildFeatures {
	compose true
  }
  composeOptions {
	kotlinCompilerExtensionVersion '1.0.1'
  }
}



 With this definition we can compose our dependencies in the new modules.

module.gradle


plugins {
  id 'com.android.library'
  id 'org.jetbrains.kotlin.android'
  id 'kotlin-kapt'
}

apply from: "$rootDir/buildsystem/android_lib.gradle"

android {
  buildFeatures {
	compose true
  }

  composeOptions {
	kotlinCompilerExtensionVersion '1.0.1'
  }
}

dependencies {

  implementation libs.core
  implementation libs.appcompat
  implementation libs.material

  //COMPOSE
  implementation libs.composeActivities
  implementation libs.composeMaterial
  implementation libs.composeConstraintLayout
  implementation libs.composeTooling
  implementation libs.composeRuntime
  implementation libs.composeViewModel

  //Room
  implementation libs.roomRuntime
  implementation libs.roomKtx
  kapt libs.roomCompiler

  //Koin
  implementation libs.koin
  implementation libs.koinJavaCompat
  implementation libs.koinCompose
  implementation libs.koinCore

  //TEST
  testImplementation testLibs.junit
  testImplementation testLibs.coroutinesTest
}

We can even take advantage of Gradle to compose even more dependencies and not have to add or update many files when adding a new one shared by several modules.

  Mobile Architecture Vision: Binder-Events

Use the Readme as a knowledge base

Within our repositories the Readme.md file usually contains steps to compile the project, it is the most common in closed projects. The most elaborated documentation is usually found in external solutions such as Confluence, Notion, etc. An interesting option is to have in each module a Readme file that contains some basic information. This information can be a description of the module’s functionality, its dependencies on other modules (with their relative link to facilitate navigability), relevant external dependencies, possible improvements to be made, etc. This makes it easier for new members to join the team, just by giving them access to the repository they can navigate through the simple documentation without having to navigate through code that is much more confusing and intimidating. One consideration is that Android Studio, at the time of writing this, does not have good MD support. A good alternative is Visual Studio Code where you can preview how your file would look in the repository.

Have an Android Project in mind? Get a free consultation

Conclusion

In conclusion, Android app modularization can be a daunting task, but it is a necessary one if you want to improve its performance, scalability, and maintainability. By following the tips outlined in this article, such as identifying the app’s key features and breaking them down into smaller modules, you can make the process more manageable. Additionally, utilizing dependency injection, decoupling components, and establishing clear boundaries between modules can help ensure that the modules are independent and can be developed, tested, and deployed separately.

With these techniques in mind, you can modularize your old Android app and unlock a whole host of benefits, including faster development cycles, easier maintenance, and improved performance. So, don’t be afraid to take on the challenge of modularization and transform your app into a more efficient and scalable product.

If you are interested in reading more articles on technical topics, I suggest you check out Apiumhub´s blog. New and interesting content gets published every week not only about Android app modularization but also about software architecture, mobile software development, quality assurance and automation, and iOS app development.

 

Author

  • Albert

    Android developer with more than 8 years of experience in various projects using innovative techniques, architectures and clean code.

    View all posts

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

Subscribe To Our Newsletter

Get updates from our latest tech findings

Have a challenging project?

We Can Work On It Together

apiumhub software development projects barcelona
Secured By miniOrange