Building Composable Modular Android Application

Wisnu Kurniawan
5 min readJan 27, 2020

How might we structure our android application project developed by multiple teams which always keep doing “continuous sprinting”? There will be many conflicts happen, added technical debt, added complexity, and then our main enemy which we have already known together will come up…

🐛

Code ownership is the answer. The good solution for code ownership is by modularization.

Modularize
/ˈmɒdʒ.ə.lə.raɪz/

verb
To design or produce something in separate sections

Modularizing application is the process separating code program into a specific independent component. The advantages of modularization:

  • Better code ownership. Each team can focus on developing its own module without worry affecting others team.
  • Reduce build time by supporting composable module. With composable module we able to build the application for a particular module instead of including all module.
  • Reduce APK size. This is related to distribution new feature with android app bundle. We can ship our application without including all feature and make it downloadable based on demand.

Apart from that, building modular application is not easy. Based on my experience, there are many things that previously were easy to become difficult. For example, navigation between module. We can not access code in another module and we can not just make some module depend on another module. This will cause more serious problems like circular dependency and tightly couple between module. If our modules getting tightly coupled there is no point about modularization better change back using the old way.

In this article, I will share how to structurize your modular application to avoid tightly coupled between a module and make it composable.

Study Case Building Ordering Ticket Application

Ordering ticket application that we want to build has multiple features. Each feature developed by a specific team and separated by a module.

  • Order train ticket feature
  • Order flight ticket feature
  • Order hotel ticket feature
  • Transaction history feature
  • Setting feature

We also need to think. How to support composable build. For example, a team that developing order train ticket want to build only their own module which is order train feature without including another feature. How to integrate a new feature. For example, in the future, there is a new feature ordering bus ticket.

Preview before after add feature Order Bus

Define Module Layers Responsibility

Feature module is a module that has business logic inside. We don’t want between feature module depend on each other because it will lead to tightly coupled and hard to maintain in the future. If a feature module want to access some component in another feature module, we separate the component into library module.

Modul layers
  1. Main Layer
    Main layer is the top layer. This layer is main android application where Feature modules are arranged together.
    - Can depend on all module from all below layers.
  2. Feature Layer
    Feature layer is the second layer. This layer is a layer of the modules that contain UI (e.g, Activity, Fragment) and have business logic inside. The module in this layer should be easy to take out or take in from Main layer.
    - Can depend on the below layer.
    - Can not depend on other feature.
  3. Library Layer
    Library layer is the last layer. Used for sharing component between Feature modules.
    - Can not depend on the above layer.

Implementation

Disclaimer at this step I only explained the important parts and I also provided link to commit from the project.

Create an abstraction between app module and feature module.

This is to make our app module not tightly coupled with feature module. If we take a look at the design for a feature, it has title, icon, and activity. So let's create contract for that.

The concept is pretty simple. In the end, we build list of FeatureLaunchable and then show the view based on that. To do that, we create a new contract name Launcher at the library layer named Launcher also. Launcher accepts a list of FeatureLaunchable during initialization. When our app launches, it creates an instance of Launcher and share the reference into all feature module, then each feature register into it.

This concept name is “Plugin” architecture. The app module provides the socket and feature module that wants to register into app module just plug into the socket.

For implementation, please see this commit.

Implement dynamic code loading

This is the main part of how to make our module can be composable. As we know we already have list of FeatureLaunchable.

The concept is pretty simple the list of FeatureLaunchable it will be same with the list of feature dependencies and this is feature dependencies code in our build.gradle.

So whenever we update list of feature in project.ext.config we want it to reflect our list of FeatureLaunchable without compile error. To achieve that, we need to implement dynamic code loading. Dynamic code loading used for check if the code exists or not. There are several approaches implement dynamic code loading.

  1. Using Reflection to create an instance by inspecting class name.
  2. Using ServiceLoader by Android to create an instance by placing a provider-configuration file in the resource directory META-INF/services.
  3. Using Dagger2 to instantiate the object graph.

I am using reflection because this is the simplest than others. Unfortunately, reflection comes with a bad performance impact. Reflection slows down the process. To handle that, I use code generation using JavaPoet library to write the list of FeatureLaunchable at compile-time and for implementation, please see this commit.

Integrate new feature.

Integrate new feature is simple.

  • We just need to create the module and implement the contract. For implementation, e.g adding feature Order bus ticket please see this commit.
  • Then register the feature. For implementation, e.g register feature Order bus ticket please see this commit.

Composable Module Demo

Now we can easily build our app with a specific feature instead of including all feature.

Full project can be found at my github:

Conclusion

Composable module can help an application developed by multiple teams. So that the developer can focus on a particular module he is developing 🤝 and it will solve the common issue regarding app build time.

Reference:

Thanks for reading this article! claps 👏 if this article helpful.

--

--