Chapter 5. Managing Multimodule Builds

Android Studio allows you to create modules for not just apps and libraries, but also for Android Wear, Android TV, Google App Engine, and more. All of these modules can be used together in a single project. For example, you might want to create an app that uses Google Cloud Endpoints for the backend and includes integration with Android Wear. In that case, you could have a project with three different modules: one for the app, one for the backend, and one for the Android Wear integration. Knowing how multimodule projects are structured and built can speed up your development cycle significantly.

Note

The documentation for Gradle and the Gradle Android plugin both use the term multiproject builds. In Android Studio, however, there is a distinction between a module and a project. A module can be an Android app or a Google App Engine backend, for example. A project, on the other hand, is a collection of modules. In this book, we use the terms module and project in the same way the IDE does, to avoid confusion. Keep this in mind when you browse through the documentation.

In this chapter, we will cover the theory of multimodule builds, and then show some examples that can be useful in real-life projects:

  • Anatomy of a multimodule build
  • Adding modules to a project
  • Tips and best practices

The anatomy of a multimodule build

Usually, a multimodule project works by having a root directory that contains all modules in subdirectories. To tell Gradle how the project is structured, and which directories contain modules, you need to provide a settings.gradle file in the root of the project. Each module can then provide its own build.gradle file. We already learned how settings.gradle and the build.gradle files work in Chapter 2, Basic Build Customization, so here we will just focus on how to use them for multimodule projects.

This is what a multimodule project could look like:

project
├─── setting.gradle
├─── build.gradle
├─── app
│    └─── build.gradle
└─── library
     └─── build.gradle

This is the simplest and most straightforward way to set up a project with multiple modules. The settings.gradle file declares all the modules in the project and looks like this:

include ':app', ':library'

This makes sure that the app and library modules are included in the build configuration. All you need to do is to add the name of the directory of the module.

To add the library module as a dependency to the app module, you need to add this to the build.gradle file of the app module:

dependencies {
    compile project(':library')
}

In order to add a dependency on a module, you need to use the project() method, with the module path as the parameter.

If you want to use subdirectories to organize your modules, Gradle can be configured to suit your needs. For example, you could have a directory structure that looks like this:

project
├─── setting.gradle
├─── build.gradle
├─── app
│    └─── build.gradle
└─── libraries
     ├─── library1
     │    └─── build.gradle
     └─── library2
          └─── build.gradle

The app module is still located in the root as it was earlier, but the project now has two different libraries. These library modules are not located in the project root directory, but in a specific libraries directory. With this directory structure, you can declare the app and library modules in settings.gradle like this:

include ':app', ':libraries:library1', ':libraries:library2'

Notice how easy it is to declare modules within a subdirectory. All paths are relative to the root directory (where the settings.gradle file is). The colon is used as a replacement for the forward slash in the path.

When adding a module in a subdirectory as a dependency to another module, you should always reference it from the root. This means that if the app module in the previous example depends on library1, the build.gradle file for the app module should look like this:

dependencies {
    compile project(':libraries:library1')
}

If you declare dependencies in a subdirectory, all paths should still be relative to the root directory. The reason for this is that Gradle constructs your project's dependency model starting at the root of the project.

The build lifecycle revisited

Knowing how the build process model is constructed makes it easier to understand how multimodule projects are composed. We already talked about the build lifecycle in Chapter 1, Getting Starting with Gradle and Android Studio, so you already know the basics, but some details are important specifically for multimodule builds.

In the first phase, the initialization phase, Gradle looks for a settings.gradle file. If this file does not exist, Gradle assumes that you have a single module build. If you have multiple modules though, the settings file is where you can define the subdirectories that contain the individual modules. If those subdirectories contain their own build.gradle files, Gradle will process those, and merge them into the build process model. This explains why you should always declare dependencies on a module with a path relative to the root directory. Gradle will always try to figure out dependencies from the root.

Once you understand how the build process model is put together, it becomes clear that there are several strategies to configure multimodule project builds. You could configure the settings for all the modules in a build.gradle file in the root directory. This makes it easy to get an overview of the entire build configuration for a project, but it could get very messy, especially when you have modules that require different plugins that each have their own DSL. Another way is to have build.gradle files separately for every module. This strategy makes sure that the modules are not tightly coupled to each other. It also makes it easier to track build changes, because you do not need to figure out which change applies to which module.

The last strategy is the hybrid approach. You can have a build file in the root of the project to define common properties for all modules, and a build file per module to configure settings that apply only to that specific module. Android Studio follows this approach. It creates a build.gradle file in the root directory, and another build.gradle file for the module.

Module tasks

As soon as you have multiple modules in your project, you need to think twice before running tasks. When you run a task from the root of the project in the command-line interface, Gradle will figure out which modules have a task with that name and execute it for every module. For example, if you have one mobile app module and one Android Wear module, running gradlew assembleDebug will build the debug version of both the mobile app module and the Android Wear module. When you change the directory to one of the modules, however, Gradle will only run the tasks for that particular module, even when you use the Gradle wrapper in the root of the project. For example, running ../gradlew assembleDebug from the Android Wear module directory will only build the Android Wear module.

Switching directories to run module-specific tasks can get annoying. Luckily, there is another way. You can prepend a task name with the module name to run the task only on that specific module. For example, to build the Android Wear module only, you can use the gradlew :wear:assembleDebug command.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.16.79.65