Android library development - Getting started

28 Oct 2020

Android library development - Getting started

4 minute read

Having switched to Android SDK development over the past year, I’ve run into quite a few interesting and unexpected challenges. So how does library development differ from app development?

This mini-series will cover the differences between SDK and App development, and explore some interesting challenges around SDK modularization and transitive dependencies.

This blog post is part of a series on Android libraries:

Introduction

Before kicking off the meat of this series, let’s have a quick look at how SDK development differs from app development. If you’re already familiar with these concepts, feel free to skip to the next article!

Note that the term SDK or Android library will be used interchangeably.

Anatomy of a library

A getting started Android SDK project typically contain of at least two modules:

  • app module to test out the SDK
  • library module that contains all SDK code and resources

Project with app and library module

In this, the app module has a direct dependency on the library module:

dependencies {
  implementation project(':library')
}

And the folder structure looks like this:

.
├── app
└── library

The library module has the com.android.library plugin in its build.gradle file:

apply plugin: 'com.android.library'

Notice how everything so far is exactly the same as developing a submodule in a multi-module app!

Building a library

Similar to building the application, the library can be built using a Gradle task:

./gradlew :library:assembleRelease

However, the output won’t be an .apk file (or .aab when using App Bundles). Instead it will be an Android Archive (or .aar) file, placed in the build folder of the library project:

library/build/outputs/aar/library-release.aar

This .aar file is very similar to a Java Archive (.jar) file, but it can also contain Android XML resources.

Note that .aar files aren’t signed, so in contrast to creating an .apk file, no signing config is required to create the release variant of the Android library.

Deploying a library

However, customers shouldn’t directly copy-paste the SDK source code into their project. Instead, they should consume the library as a Maven dependency:

dependencies {
  implementation "com.jeroenmols:library:1.0.0"
}

Resulting in the following project setup:

App that consumes library through Maven

To make this possible, the library needs to be deployed to a public Maven repository such Maven Central or Bintray (jcenter). Which can be done by configuring the Gradle publishing plugin.

This blog post won’t cover how to publish your library, instead have a look at this great article by Andrew Kelly if you’re looking to learn.

External dependencies

But as the library evolves, it might also start depending on Maven dependencies of its own! Imagine that the library would also depend on OkHttp:

Project with app and library module that has an external dependency

This means that the customer application needs to depend on both library and OkHttp:

Project with app and library module that has an external dependency

Why?

Because .aar files only contain code and resources of the library module that was used to build it! So the .aar file of the library won’t contain any OkHttp code, nor any indication that it requires OkHttp to run.

Consequently, customer applications need to include both the library and OkHttp as a dependency.

dependencies {
  implementation "com.jeroenmols:library:1.0.0"
  implementation "com.squareup.okhttp3:okhttp:4.9.0"
}

Notice that this did work when library is a submodule of a project! Then the build.gradle file of the library includes the OkHttp dependency and Gradle will include it into the .apk while building.

Transitive dependencies

Wouldn’t it be nice if the OkHttp dependency could be automatically included in the customer application?

That way customers simply have to add:

dependencies {
  implementation "com.jeroenmols:library:1.0.0"
}

And get the OkHttp dependency indirectly through the dependency on library. This is what we call a transitive dependency: OkHttp is a transitive dependency of the library and hence an indirect dependency of the customer application.

But how can Gradle know to include OkHttp in the customer app after adding the library as a dependency?

That’s taken care of by the pom.xml file that gets created when you deploy your app to a Maven repository (such a Maven Central).

Here’s an example pom.xml for the library:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.jeroenmols</groupId>
  <artifactId>library</artifactId>
  <version>1.0.0</version>
  <name>library</name>
  <dependencies>
    <dependency>
      <groupId>com.squareup.okhttp3</groupId>
      <artifactId>okhttp3</artifactId>
      <version>4.9.0</version>
      <scope>compile</scope>
    </dependency>
  </dependencies>
</project>

So when building an Android library, there are two key outputs:

  • .aar file: a binary containing all library code and resources
  • pom.xml file: containing all transitive dependencies

In reality, there can be many more files (Javadoc, sources,…). Have a look at the Files tab of the Maven entry for OkHttp.

For more information on how to generate a pom.xml, have a look at this post by Marco Gomiero.

Testing

Finally, for Android libraries with external dependencies, there will be a difference between a local build of the SDK or a Maven build.

If the SDK source code is within a project, then transitive dependencies will automatically be included when the application is built.

However, when the SDK is included through Maven, those transitive dependencies will only be included when the pom.xml file is properly constructed and deployed to Maven.

Therefore it is always important to test the actual SDK artifact as a Maven dependency before shipping a new release!

Wrap-up

Android libraries are built into a special .aar format that includes all code and resources. For all its external dependencies, a pom.xml needs to be deployed alongside to the Maven repository.

Don’t forget to follow me on Twitter and enjoy reading the next post on SDK modularization!

Feel free to leave a comment below!

Leave a Comment