Publishing a library as a .aar package

George Kelemouridis
Pion
Published in
9 min readFeb 11, 2021

Introduction

This article will describe a way of publishing a .aar package to Maven using GitHub and JitPack. This is NOT a guide that will help you develop the module, but the steps that you can take after finishing your development.

It will describe the reasons for the actions taken and the expected results that will come out of it.

The article includes a guide and explanation of the obfuscation process as well.

A glossary section follows, so everyone can keep up with technical terms and names.

Glossary

  • App module: Modules provide a container for your app’s source code, resource files, and app-level settings, such as the module-level build file and Android manifest file. Each module can be independently built, tested, and debugged. An independent app module can, later on, be released as a library.
  • Maven: For Android, Maven is a tool that we use to publish and access .jar, .aar and libraries to online repositories like the JitPack or Maven Central. As a term, it also describes the most widely used type of a repository.
  • JitPack: It is a novel package repository for JVM and Android projects. It builds Git projects on demand and provides you with ready-to-use artifacts (.jar, .aar). It is a public maven repository and serves maven artifacts. It helps you make your library available to the world.
  • GitHub: It is the most popular and widely used code hosting platform for version control and collaboration.
  • .aar: (Android Archive) files are a convenient way to distribute packages– mainly libraries– for use with Android Studio and Gradle.
  • Obfuscation: In software development, obfuscation is the deliberate act of creating source or machine code that is difficult for humans to understand.
  • ProGuard: It is a free Java class file shrinker, optimiser, obfuscator, and preverifier.

Main idea

Our main goal is to have our module’s .aar file published and available for any user. The user will be able to add it as a dependency on their projects and use it as an external library.

We are going to use a public GitHub repository to easily have read access for JitPack.

The library won’t be open-source but available to use.

Why publish a .aar file?

JitPack is a tool that its main purpose is to build a Git project and provide the .aar file. This is also the recommended and official practice according to them. This approach would be fine if we wanted to release an open-source library.

The case that we describe in this article includes a closed-source library. Meaning that the source code will be obfuscated before reaching our GitHub repository. It will still be free and available to anyone for use.

In order to achieve that, we need to publish our module’s .aar file.

It is important to note that the .aar file doesn’t include the nested dependencies. As a result the app that will use it, will need to import those nested dependencies as well. In order to avoid this, we’ll go through a workaround that will allow us to extract our dependencies to a pom.xml file and pass it to Jitpack.

You can always pay for a JitPack private repository, but then you’ll need to provide the auth token to the users.

Prerequisites

  • An already developed module
  • GitHub Account
  • JitPack subscription

Obfuscation

As we mention in the glossary, obfuscation is a way of masking your code. This adds a level of security. It makes your code harder to be reverse-engineered.

In Android, we can use a free tool called ProGuard. Every Android project contains a proguard-rules.pro file which is usually empty. In there you can provide rules for the classes that you need to be obfuscated.

It’s very important to be fully aware of which classes you want to be left out of the obfuscation process. Usually, those classes are the ones available for the users, or any needed interfaces.

Below is an example of a proguard-rules.pro file:

# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
-keepclassmembers class com.exampleproject.examplepackage.exampleinterfaces.ExampleInterfaceClass {
public *;
}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
# Below is an example of mentioning a class that you don't want to be obfuscated
-keep class com.exampleproject.examplepackage.ExampleClass { *; }
# The below are suggestions to be kept by ProGuard.
-keepattributes Signature
-keepattributes Exceptions
-keepattributes *Annotation*
-keepattributes EnclosingMethod
-keepattributes LineNumberTable,SourceFile

For the above file to be applied to your build, we need to add the below snippet to our module-level build.gradle file.

buildTypes {
release {
// Enables code shrinking, obfuscation, and optimization for only
// your project's release build type.
minifyEnabled true

// Enables resource shrinking, which is performed by the
// Android Gradle plugin.
// Shrink resources doesn't work for modules/libraries
shrinkResources false

// Includes the default ProGuard rules files that are packaged with
// the Android Gradle plugin. To learn more, go to the section about
// R8 configuration files.
proguardFiles getDefaultProguardFile(
'proguard-android-optimize.txt'),
'proguard-rules.pro'
}
}

And with the above, the obfuscation should be set and ready to run.

Maven publish tool

Android Gradle plugin 3.6.0 and higher include support for the Maven Publish Gradle plugin, which allows you to publish build artifacts to an Apache Maven repository. The Android Gradle plugin creates a component for each build variant artifact in your app or library module that you can use to customize a publication to a Maven repository.

The components that the Android plugin creates depend on whether the module uses the application or library plugin.

Just to keep a clean structure to our code, we can create a new Gradle task file to put our publication code. The file will be named publish.gradle and will look like this:

apply plugin: 'maven-publish'def LIB_GROUP_ID = 'com.group.module'
def LIB_ARTIFACT_ID = 'final'
def LIB_VERSION = '1.0'
task sourceJar(type: Jar) {
from android.sourceSets.main.java.srcDirs
classifier "sources"
}
publishing {
repositories {
maven {
name = 'CustomMavenRepo'
url = "file://${buildDir}/repo"
}
}
publications {
release(MavenPublication) {
groupId LIB_GROUP_ID
artifactId LIB_ARTIFACT_ID
version LIB_VERSION
artifact("$buildDir/outputs/aar/module-debug.aar")
artifact(sourceJar)
pom.withXml {
def dependenciesNode = asNode().appendNode('dependencies')
//Iterate over the compile dependencies (we don't want the test ones), adding a <dependency> node for each
configurations.api.allDependencies.each {
def dependencyNode = dependenciesNode.appendNode('dependency')
dependencyNode.appendNode('groupId', it.group)
dependencyNode.appendNode('artifactId', it.name)
dependencyNode.appendNode('version', it.version)
}
}
}
}
}

Taking a closer look at the above snippet, we see the task sourceJar(type: Jar) which helps us generate our sources’ .jar files.

Inside the publishing task, we can find the repositories. Here we declare our custom repo and we’ll see how it’s used later in the Creating the publishing files section.

The most important part comes with the pom.withXml method, which checks our dependencies from our build.gradle file and creates dynamically the pom.xml.

It is important for this to work, that the dependencies that needed to be passed to the pom.xml file, would be declared with the “api“ keyword instead of “implementation“ so we can expose them as transitive dependencies.

example: api ‘androidx.fragment:fragment-ktx:1.2.5’

Fore more information about this, you can check the official documentation here.

The next step is to return to our module’s build.gradle file and call this task following the below snippet:

plugins {
id ....
.....
....
}
apply from: file('publish.gradle')
...
repositories {
....
}
...
android {
....
}
dependencies {
....
api ....
api ....
implementation ....
....
}

Creating the .aar files

Gradle tasks

After completing the above and sync your project, you will be able to run the publish[Publication]ToCustomMavenRepoRepository Gradle task. This can be easily found in the Android Studio, on the right side menu called Gradle, as shown in the screenshot. Please note that you need to run the task that is located under your module and not the one under the main app. After the task is finished you can find the .aar files in the $buildDir/repo/

Example of package structure

Upload .aar on Github

Given that you already have a GitHub account, you need to create a new repository.
Given that you have already assembled your module and created your release .aar file, you need to upload it in your new GitHub public repository.

Upload an existing file
Choose your files

Locate your release .aar and commit the changes.

Commit changes

After that, your repository will look like this:

Repository view

JitPack workaround

As we mentioned before, JitPack default behaviour is to build a Git project and then publish the .aar file. So for our purpose, we need to let JitPack know that we provide an already prepared .aar file. To do that we need to add 2 files to our GitHub repository, at the same level as the .aar file. Those files are:

jitpack.yml

install: 
FILE="-Dfile=test.aar"
mvn install:install-file $FILE -DgroupId=com.group.module -DartifactId=test -Dversion=1.0 -Dpackaging=aar -DpomFile=pom.xml

You will notice that with the last command we declare that Jitpack should use the pom.xml that we provide, and not generate its own.

pom.xml

In the pom.xml file we can paste the information that our local pom file contains. For example in your Android Studio you can navigate to $buildDir/repo/com/group/module/final/1.0/final-1.0.pom

<?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.group.module</groupId>
<artifactId>final</artifactId>
<version>1.0</version>
<dependencies>
<dependency>
<groupId>androidx.appcompat</groupId>
<artifactId>appcompat</artifactId>
<version>1.2.0</version>
</dependency>
<dependency>
<groupId>com.google.android.material</groupId>
<artifactId>material</artifactId>
<version>1.2.1</version>
</dependency>
</dependencies>
</project>

As you can see, you’ll need to update a few points on those files to much your repository and .aar file.

After creating those files, you’ll follow the same process as earlier to upload them to your GitHub repository. The repository will look like this:

Updated repository view

JitPack

Our next step will be to connect our GitHub account with JitPack. To do that you need to sign in here with your GitHub credentials. When you sign in, JitPack will read all your public repositories from GitHub as shown below and then you can look up a specific URL.

JitPack Look-up

At the moment, you can’t see anything else there because you haven’t created a release on GitHub yet.

GitHub Release

Our next step would be to create a release on GitHub. The process is simple.

In your repository page, you click on Releases.

GitHub Releases

Then Draft a new release.

GitHub Draft new release

Then you fill in the needed details and click on Publish release.

Github Publish release

Now you created a new release of your library and we are ready to check it on JitPack.

JitPack Release Build

Whenever you do a new release on GitHub, you can see it on JitPack. Right next to the version number you find the logs.

JitPack Log

In the logs, you can see when the build process finishes if the build is successfully published or not. If it’s not you can check the errors and act accordingly. If the build was successful, then you move to the final step.

Use the library to a new app

If all the previous steps were successful, then your library is published and it’s ready to use. To do that, you need to go to the app-level build.gradle and add the following 2 snippets:

repositories {     
...
maven { url 'https://jitpack.io' }
...
}

and

dependencies {     
implementation ('com.github.group:library:latest-version@aar'){transitive=true}
}

After that, you can sync your project… and… magic!

Your library is added as an External Library to your new project and it’s ready for use.

--

--