Android Modular Project – Organizing your library dependencies

As your project grows you might have found the need to split it into several modules. This becomes even more prominent when you’re working in a company where several teams develop the same app, but different features.
Library books upstairs

It’s not so uncommon to have the same library dependencies accross the modules. Usually modules tend to use the same library to achieve similar things and keep the number of used libraries to a minimum. However, it’s also not so uncommon to accidentally use different versions of the same library in this setup.

Here at Babbel we recently faced this issue and in this blog post I’ll share with you a possible solution to overcome this.


Approaching the problem

Suppose you’re developing an app that for some reason needs to be split into several modules. Perhaps there’s a certain functionallity that you want to isolate in a single module, or maybe you want to split features accross teams and this setup helps you with that.

Let’s think of a concrete example. Say you wish to split tracking into a single module. Given the following project structure:

MyApp
 |
  \ app
 |
  \ tracking

Here MyApp is the project you’re working on. app is the main application code and tracking is the separated module.

Each of these modules has its own build script. Likewise, the main project has also its own build script. Here’s a diagram showing this:

MyApp
 |
  \ app
 |   |
 |    \ build.gradle
  \ tracking
 |   |
 |    \ build.gradle
 |
  \ build.gradle

This means that each module has its own place for setting compile dependencies. It’s then fair to assume that it’s not so hard to fall into the situation where the app module uses version 1.0.0 of library xyz and the tracking modules uses version 1.0.1 of the same library.

Here at Babbel we ran into the same situation quite recently and applied the solution described in the next section.

Keep your libraries organised

Once you look at the problem it’s easy to understand that it exists because there are multiple places where you can specify compile dependencies with string literals.

MyApp/app/build.gradle

dependencies {
    compile 'com.babbel:xyz:1.0.0'
}

MyApp/tracking/build.gradle

dependencies {
    compile 'com.babbel:xyz:1.0.1'
}

If there would be a common place from where you could take these string literals, then the problem would be mitigated. We opted to use the project extra properties extension. In the end our build files look something like:

MyApp/app/build.gradle

dependencies {
    compile project.libraries.xyz
}

MyApp/tracking/build.gradle

dependencies {
    compile project.libraries.xyz
}

We create the extra property libraries in the project wide build.gradle file like so:

MyApp/build.gradle

class Libraries extends Expando {}

project.ext.libraries = new Libraries()

project.ext.libraries.xyz = 'com.babbel:xyz:1.0.1'

The Expando class lets you pin down attributes to the class way easier by just setting them with the dot notation as it’s shown in the last line. We went a step further and avoided polluting the project wide build.gradle file by creating a separate file called libraries.gradle at the same level as the project wide build.gradle and used the following to apply it to the entire project:

MyApp/build.gradle

apply from: 'libraries.gradle'

// ...

The Pros and Cons

The obvious advantage of this solution is the fact that now you have all your dependencies in a single place accessible by every module in your project. At the same time it becomes a point for conflicts so one must be careful when updating the libraries.

Another point I consider an advantage is that now you can easily force all modules to be updated once a new libary comes out and not having any of them trailing behind. A lot of the updates are backwards compatible so the impact won’t even be noticeable. For updates that are not backwards compatible it might take a bit longer to update the modules in a single go, but at least at the end you’ll have the guarantee that all your modules are up to date.

Last but not least, we saw a great improvement in the build times. Since now you have less libraries to download and compile it’s very likely that your build time will be less than before even with gradle’s incremental builds.

Summary

In this blog post I showed you a possible solution for organising your library dependencies in a modular Android project. It’s important to notice that this is not the sole solution and that there might be others that better fit your needs.

Photo by Anna Hunko on Unsplash

Want to join our Engineering team?
Apply today!
Share: