Working with Library Modules

Android library modules are the primary unit of Android source reuse, particularly where that source involves more than just Java source code, such as Android resources.

In this chapter, we will explore the basics of setting up and using an Android library module.

Prerequisites

Understanding this chapter requires that you have read the core chapters of this book.

Creating a Library Module

An Android library module, in many respects, looks like a regular Android project. It has source code and resources. It has a manifest.

What it does not do, though, is build an APK file. Instead, it represents a basket of programming assets that the Android build tools know how to blend in with regular Android projects.

Making a project be an Android library module is simply a matter of choosing the right Android Gradle Plugin.

Rather than have:


apply plugin: 'com.android.application'

use:


apply plugin: 'com.android.library'

That’s it — the com.android.library plugin now knows that it is creating a library, not an app.

The real question is, where are you making this library? In many cases, you will do so as a module in a project, where there is another module that is an app. This covers both:

Adding new modules to an Android Studio project is handled most simply via the new-module wizard, which you can bring up via File > New > New Module… from the main menu. This brings up the first page of the new-module wizard:

Android Studio New-Module Wizard, First Page
Figure 312: Android Studio New-Module Wizard, First Page

To add a library module as a module to an existing project, choose “Android Library” in the list of module types, then click Next to proceed to the second page of the wizard:

Android Studio New-Module Wizard, Second Page
Figure 313: Android Studio New-Module Wizard, Second Page

This collects some bits of information, including:

At this point, clicking Next will take you to the same new-activity flow that you saw when creating a new project. If you want an activity to be generated for you in this library, proceed by selecting the activity template and providing the activity template configuration data. If you do not want an activity, choose “Add No Activity” in the grid of templates, then click “Finish” to create the module.

In the end, the new-module wizard will set up the new module for you, in your designated subdirectory of the project, including modifying settings.gradle to list this subdirectory as being a module within the project. At this point, you will be able to start using the library within the project itself.

Using a Library Module

Once you have a library module, you can attach it to another Android module, so the other Android module has access to everything in the library. This works similarly to adding other sorts of dependencies, with a slight syntax change. Rather than use an artifact identifier or fileTree(), we use project():


implementation project(':libname')

where libname is the name of the module. This will be the directory name, and it will also show up as the module’s name in settings.gradle in the project root directory:


include ':app', ':libname'

The leading colon means that this module is directly off of the project root directory.

If you wish to use this library module in separate projects, other than the one that hosts the module, you will need to distribute it to those other projects. A typical way to do that is to compile the library module into an AAR file and have that be hosted in an artifact repository.

Library Modules and the Manifest

Library projects can publish their own AndroidManifest.xml file, which contributes to the overall manifest used by apps that incorporate the library. Hence, a library can:

However, merging these manifests is a rather complex topic, and as such will be covered much later in the book.

Library Modules and Transitive Dependencies

Just as an application module can have dependencies, so can a library module. For other modules that depend upon the library module, the library module’s dependencies become transitive dependencies. In other words, if App A depends upon Library B, and Library B depends upon Library C and Library D, App A has transitive dependencies on Library C and Library D.

When a module depends directly upon a library module (e.g., implementation project(':libname')), all of that library module’s dependencies get added to the requesting module’s dependencies list. The same holds true when a module depends upon a library from an artifact repository. It does not hold true for bare libraries, such as adding plain JARs to a project via implementation fileTree(...).

There are three key ways that a library module can declare a dependency: implementation, api, and compileOnly. While an app module typically would only use implementation, all three of these options are possible for a library module, with subtly different behavior:

Configuration Name Is Dependency’s API Available to Hosts? Is Dependency’s Code Added to Hosts?
implementation no yes
api yes yes
compileOnly no no

Let’s examine a concrete example: CWAC-NetSecurity. This library — which will be profiled later in the book — has its own code, plus it has helper code for integrating the library’s code with OkHttp. There are three ways that CWAC-NetSecurity could declare its dependency upon OkHttp, and those choices change what happens in an app that itself depends upon CWAC-NetSecurity:

implementation and api are the two most common. compileOnly is mostly for cases where the library module would like to offer integration for some third-party library, but where that library is not essential. For apps that want to use the integration, they simply need to have an additional dependency. For apps that do not want the integration, though, they avoid having all the extra code added from the third-party library.

Limitations of Library Modules

While library modules are useful for code organization and reuse, they do have their limits.

As noted above, if more than one project (main plus libraries) defines the same resource, the higher-priority project’s copy gets used. Generally, that is a good thing, as it means that the main project can replace resources defined by a library (e.g., change icons). However, it does mean that two libraries might collide. It is important to keep your resource names distinct to minimize the odds of this occurrence.