

The Complete Guide to Creating and Publishing an Android Library
I was always amazed at how many useful third party libraries the Android developer community has produced. And for the longest time, I wanted to contribute something but didn’t know how. After browsing though other blog posts about how to publish an Android Library I still found that there were some missing details and that all the information was in different places. So I am going to go through this process and show what worked for me.


For starters, I use Android Studio, which uses Gradle as the official build system, to set up all my Android projects. Make sure to download the latest version of Android Studio .
Terminology
Before we begin there is some terminology to get familiar with.
Project — A project in Android Studio represents a complete Android app. Android Studio projects consist of one or more modules. A project in Android Studio is like a workspace in Eclipse.
Module — A module is a component of your app that you can build, test, or debug independently. Modules contain the source code and resources for your app. A module in Android Studio is like a project in Eclipse
AAR — The ‘aar’ bundle is the binary distribution of an Android Library Project. (AAR Format) A Library project’s main output is an .aar package (which stands for Android archive). It is a combination of compile code (as a jar file and/or native .so files) and resources (manifest, res, assets).
Maven Central Repository — A repository provided by the Maven community. It contains a large number of commonly used libraries. Search Maven to browse the content of the central maven repository. Gradle, Please is another tool that allows you to search Maven Central. Gradle uses the jCenter repo if you included jCenter() into your repositories section (Notes about jCenter). Maven Central Repository is often referred to as Maven Central or the Central Repository.
Sonatype — Sonatype’s Open Source Software Repository Hosting (OSSRH) service is the primary avenue for project owners and contributors to publish their components to the Central Repository. It is a hosted deployment of Sonatype Nexus Professional with the Nexus Staging Suite used for the deployment process and validation, combined with a sync process to the Central Repository content delivery network. (Sonatype Producers)
GPG — GNU Privacy Guard (GPG, also GnuPG), the GNU project’s free alternative to PGP, is encryption software that’s compliant with the OpenPGP (RFC4880) standard. Using GPG you can encrypt (and decrypt) files that contain sensitive data, such as electronic protected health information (ePHI) regulated by the Health Insurance Portability and Accountability Act (HIPAA) privacy and security rules. For more on GPG, see the GNU Privacy Guard website.
Prepare your Android library
I am going to use my Trestle library as an example. There are a few modifications you need to make to your project so that it is ready to be published as a library to Maven Central.
- Separate the code that is core to the library and code that shows sample usage of the library. In my projects, I separate these out into two separate modules library and sample. Checkout out the tips on setting up a library module . You may need to rename your modules.
- In the sample module’s build.gradle file, make sure to include
apply plugin: 'com.android.application'
dependencies {
compile project(':library')
}- In the library module’s build.gradle file, make sure to include
apply plugin: 'com.android.library'
apply from: 'maven-push.gradle'
- In the library module, add a gradle.properties file, and make sure to include
POM_NAME=ProjectName
POM_ARTIFACT_ID=projectname
POM_PACKAGING=aar
- In the library module, add a maven-push.gradle file, and make sure to include
/*
* Copyright 2013 Chris Banes
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
apply plugin: 'maven'
apply plugin: 'signing'
def isReleaseBuild() {
return VERSION_NAME.contains("SNAPSHOT") == false
}
def getReleaseRepositoryUrl() {
return hasProperty('RELEASE_REPOSITORY_URL') ? RELEASE_REPOSITORY_URL
: "https://oss.sonatype.org/service/local/staging/deploy/maven2/"
}
def getSnapshotRepositoryUrl() {
return hasProperty('SNAPSHOT_REPOSITORY_URL') ? SNAPSHOT_REPOSITORY_URL
: "https://oss.sonatype.org/content/repositories/snapshots/"
}
def getRepositoryUsername() {
return hasProperty('NEXUS_USERNAME') ? NEXUS_USERNAME : ""
}
def getRepositoryPassword() {
return hasProperty('NEXUS_PASSWORD') ? NEXUS_PASSWORD : ""
}
afterEvaluate { project ->
uploadArchives {
repositories {
mavenDeployer {
beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }
pom.groupId = GROUP
pom.artifactId = POM_ARTIFACT_ID
pom.version = VERSION_NAME
repository(url: getReleaseRepositoryUrl()) {
authentication(userName: getRepositoryUsername(), password: getRepositoryPassword())
}
snapshotRepository(url: getSnapshotRepositoryUrl()) {
authentication(userName: getRepositoryUsername(), password: getRepositoryPassword())
}
pom.project {
name POM_NAME
packaging POM_PACKAGING
description POM_DESCRIPTION
url POM_URL
scm {
url POM_SCM_URL
connection POM_SCM_CONNECTION
developerConnection POM_SCM_DEV_CONNECTION
}
licenses {
license {
name POM_LICENCE_NAME
url POM_LICENCE_URL
distribution POM_LICENCE_DIST
}
}
developers {
developer {
id POM_DEVELOPER_ID
name POM_DEVELOPER_NAME
}
}
}
}
}
}
signing {
required { isReleaseBuild() && gradle.taskGraph.hasTask("uploadArchives") }
sign configurations.archives
}
//task androidJavadocs(type: Javadoc) {
//source = android.sourceSets.main.allJava
//}
//task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) {
//classifier = 'javadoc'
//from androidJavadocs.destinationDir
//}
task androidSourcesJar(type: Jar) {
classifier = 'sources'
from android.sourceSets.main.java.sourceFiles
}
artifacts {
archives androidSourcesJar
}
}
- Modify the .gitignore file that is located at the root level of your project
# [Android] ========================
# Built application files
*.apk
*.ap_
# Files for the Dalvik VM
*.dex
# Java class files
*.class
# Generated files
bin/
gen/
# Gradle files
.gradle/
build/
# Local configuration file (sdk path, etc)
local.properties
# Proguard folder generated by Eclipse
proguard/
# Log Files
*.log
## Directory-based project format:
.idea/
## File-based project format:
*.ipr
*.iws
## Plugin-specific files:
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
# [Maven] ========================
target/
pom.xml.tag
pom.xml.releaseBackup
pom.xml.versionsBackup
pom.xml.next
release.properties
# [Gradle-Android] ========================
# Ignore Gradle GUI config
gradle-app.setting
# Gradle Signing
signing.properties
trestle.keystore
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.ear
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
# Misc
/.idea/workspace.xml
.DS_Store
/captures
**/*.iml
*.class
- Modify the settings.gradle file that is located at the root level of your project
include ':sample', ':library'
- Modify gradle.properties file which is located at the root level of your project
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
# Default value: -Xmx10248m -XX:MaxPermSize=256m
# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
VERSION_NAME=0.0.1
VERSION_CODE=1
GROUP=com.github.github_username
POM_DESCRIPTION=A library that does X, Y, and Z
POM_URL=https://github.com/github_username/ProjectName
POM_SCM_URL=https://github.com/github_username/ProjectName
POM_SCM_CONNECTION=scm:[email protected]:github_username/ProjectName.git
POM_SCM_DEV_CONNECTION=scm:[email protected]:github_username/ProjectName.git
POM_LICENCE_NAME=The Apache Software License, Version 2.0
POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt
POM_LICENCE_DIST=repo
POM_DEVELOPER_ID=github_username
POM_DEVELOPER_NAME=GitHub FullName
- Add a README.md to explain to other developers what your library does and how to use it. If you need to add any screenshots to your README.md I highly recommend using an app called Screenr to generate the screenshots.
Setup GPG
If you don’t already have GPG installed on your machine then you will need to download it. On MacOSX, here are the install commands .
If you never used it — first create the GPG keys:
$ gpg --gen-key
If you aren’t sure what to answer to the questions when creating the GPG keys this guide should help (Creating an encryption key) .
Next, find your key ID :
$ gpg --list-keys
There the first line will be like pub XXXXX/YYYYYYYY <date>. Remember that ‘YYYYYYYY’ part, it’s you key ID.
Now, publish your keys:
$ gpg --keyserver hkp://keyserver.ubuntu.com --send-keys YYYYYYYY
$ gpg --keyserver hkp://pgp.mit.edu --send-keys YYYYYYYY
You may use other key servers as well. You may also want to ensure your keys were published:
$ gpg --keyserver hkp://pgp.mit.edu --search-keys [email protected] # Use your email
To have your library listed on Gradle, Please (and have people include your library easily), upload your project to Maven Central. The easiest way to do this is via Sonatype.
Sonatype


I have created a GitHub repo for my project Trestle . So I filled out the fields in the new issue like so :
Group Id : com.github.<github_username>
Project URL : https://github.com/<github_username>/<project_name>
SCM url : https://github.com/<github_username>/<project_name>.git
Username : <sonatype_username>
Already Synced to Central : No
Note: I have added brackets around fields as placeholders. You will need to substitute with the appropriate values.
When you submit the issue, the details of the issue should look similar to the screenshot above. After you submit, it can take up to 2 business days to process your issue. Then you will receive a confirmation that your configuration has been prepared and you can publish your library.
Do not deploy until after you have received an e-mail notice indicating that the ticket is Resolved. One of the most common problems related to new projects is premature deployment, which misroutes your artifacts to a catch-all repository.
Lastly, if your component is already in Central be sure to note this in your ticket, and review how to migrate to OSSRH.
The SonaType OOSRH Guide has more details about this process.
Modify a file on your local machine ~/.gradle/gradle.properties and include
NEXUS_USERNAME=sonatype_username
NEXUS_PASSWORD=sonatype_password
signing.keyId=gpg_key_id
signing.password=gpg_password
signing.secretKeyRingFile=/Users/username/.gnupg/secring.gpg
org.gradle.daemon=true
Authentication is provided by this gradle.properties when publishing the library. Make sure you have provided the correct nexus username and password (this is the Sonatype user name and password) otherwise you get an unauthorized 401 error .
Note : If you have already published a library then you won’t need to create a new issue on JIRA(Sonatype). Only one JIRA issue per top-level groupId is necessary. You should already have all the necessary permissions to deploy any new artifacts to your groupId or to any sub-groups. The sync to Central applies from your top level groupid on down, so any releases to sub groups will automatically sync. There’s no need to tell Sonatype when you release a new component, since there’s nothing left for Sonatype to configure or check once your repo syncs, and they only post to twitter when the sync is first activated.
Publish


Once you are ready to publish your library, in Android Studio open the Gradle View on the right side. Under Tasks > upload click on uploadArchives. This will upload your library to the Sonatype Staging Repositories .
Make sure you are logged in with your Sonatype account when going to the Sonatype Staging Repositories webpage. Look for your “staging” library. It should be at the end of the list. Select it, and press “Close” button. Closing a library actually means that you’re ready to release it. If closing went fine — you should see a ‘Release’ button active. You may need to refresh the page. Press the release button. Checkout the helpful documentation on Repository Management with Nexus. If this is the first library that you are publishing then get back to JIRA and post a comment there that you promoted you library. Otherwise it is unnecessary to let Sonatype that you promoted your library. After that you should get a response from Sonatype that your library will be available in about ten minutes and it will be synced with the Maven Central in the next few hours. It may take a few hours before it shows up on Gradle, Please .
Library Usage
For other developers to use your library they will need to add a dependency to the build.gradle file of their Android project.
apply plugin: 'android'
dependencies {
compile 'com.github.lawloretienne:trestle:0.0.3'
}Shoutout
Special thanks to Chris Banes, Jonathan Le, Serge Zaitsev and others for their blog posts which helped me get through this tricky process.
Chris Banes — Pushing AARs to Maven Central
Jonathan Le — Publishing an Android Library (AAR) to a Maven Repository
Serge Zaitsev — SIMPLE LIBRARY PUBLISHING WITH GRADLE
My Libraries
- QuickReturn — https://github.com/lawloretienne/QuickReturn
- Trestle — https://github.com/lawloretienne/Trestle
- ImageGallery — https://github.com/lawloretienne/ImageGallery