24
Publishing your Android library - the local way
If you ever built an Android library, meant to be used in one or more projects, you have for sure faced the need to publish it in order to test its integration into your target app. You probably publish your libraries in some repository, such as JitPack, Nexus, JFrog, or other. But, what if you need to continue introducing changes or working on fixes in your library? Maybe publishing it to a remote repository each time is not the most time-saving option, especially if your library is a bit overweight. Or maybe your Internet access was cut off; how are you supposed to continue working like this? Well, there is actually a convenient, effective way to quickly get your library published and available to be integrated into your other projects: the Maven local repository.
Maven local is actually a repository, but it is located in your own computer. As simple as that. So you can use this directory to store your project dependencies.
Depending on your computer's operating system, the default location for Maven local repository may vary.
- Mac:
/Users/[username]/.m2
- Linux:
/home/[username]/.m2
- Windows:
C:\Users\[username]\.m2
First, we are going to navigate to our library's module, and there we are going to create a Gradle file, which will contain all the necessary code to publish the library locally. We are going to name it publishLocal.gradle
. Its content will be the following:
apply plugin: 'maven-publish'
task sourceJar(type: Jar) {
from android.sourceSets.main.java.srcDirs
classifier "sources"
}
project.afterEvaluate {
publishToMavenLocal {
def groupId = LIBRARY_GROUP
def artifactId = LIBRARY_ARTIFACT_ID
def versionName = LIBRARY_VERSION_NAME + "-local"
def debugSuffix = "-debug"
def releaseSuffix = "-release"
publishing {
publications {
LibraryRelease(MavenPublication) {
from components.release
artifact(sourceJar)
setGroupId groupId
setArtifactId artifactId
version versionName + releaseSuffix
}
LibraryDebug(MavenPublication) {
from components.debug
artifact(sourceJar)
setGroupId groupId
setArtifactId artifactId
version versionName + debugSuffix
}
}
publications.all {
pom.withXml {
asNode().dependencies.'*'
.findAll() {
it.scope.text() == 'runtime' &&
project.configurations.implementation.allDependencies.find {
dep -> dep.name == it.artifactId.text()
}
}.each { it.scope*.value = 'compile'}
}
}
}
doLast {
def prettyPrint = {
1.upto(100, { print "=" })
println()
}
println()
prettyPrint()
println "PUBLICATION FINISHED"
println "Artifact RELEASE: " + groupId + ":" + artifactId + ":" + versionName + releaseSuffix
println "Artifact DEBUG: " + groupId + ":" + artifactId + ":" + versionName + debugSuffix
prettyPrint()
}
}
}
Now we can include this script in our module-level build.gradle
file. And that's it!
...
apply from: 'publishLocal.gradle'
If you take a look at our first line of code, you can see that we are including the Maven Publish plugin, which lets us publish artifacts to an Apache Maven repository.
Below that line, we configure a task called sourceJar
, which will generate the .jar
file with our library's source files.
Now we are getting to the meaty part. We configure publishToMavenLocal
task, indicating the publications it should perform. In this case, there are two: LibraryRelease
and LibraryDebug
; each of them uses a different build variant.
LIBRARY_GROUP, LIBRARY_ARTIFACT_ID, and LIBRARY_VERSION_NAME are constants defined in gradle.properties file.
Let's see each MavenPublication
in detail:
LibraryRelease(MavenPublication) {
from components.release
artifact(sourceJar)
setGroupId groupId
setArtifactId artifactId
version versionName + releaseSuffix
}
We are taking LibraryRelease
as an example. First, we apply the component with the release build variant. After that, we indicate that besides the .aar
file that is going to be generated, we also want to compile a .jar
file with the source files (this step is optional). Finally, we set a group ID, an artifact ID, and a version name for our library (in this case we are using different suffixes for debug and release variants).
It is now time to generate a .pom
file for each of the publications. The .pom
file contains all the necessary configuration details that Maven uses to compile our project. Here we can find, among other things, the dependencies our library includes in detail.
publications.all {
pom.withXml {
asNode().dependencies.'*'
.findAll() {
it.scope.text() == 'runtime' &&
project.configurations.implementation.allDependencies.find {
dep -> dep.name == it.artifactId.text()
}
}.each { it.scope*.value = 'compile'}
}
}
In my particular case, I had to write a special rule to correct the Maven scope of some of the dependencies I use in my library. This may not be necessary for you. More information on Maven scopes here and here.
We end our script by printing a log with information about the published artifacts.
If you head to Gradle's task tree, inside the publishing section you will see we have these tasks available, which we can run depending on our needs:
-
publish[MODULE_NAME]DebugPublicationToMavenLocal
compiles the project in debug mode. -
publish[MODULE_NAME]ReleasePublicationToMavenLocal
compiles the project in release mode. -
publishToMavenLocal
generates both debug and release versions.
In the following screenshot we can observe the output of a successful execution of publishToMavenLocal
:
Gradle tasks can also be run from the command line, for example:
./gradlew publishToMavenLocal
./gradlew publishLibraryDebugPublicationToMavenLocal
Awesome! We already have our library, with its two variants and their artifacts, published in Maven local. But now, how can we integrate it into another project?
To import our library into another library or into an Android app, first we need to open our project-level build.gradle
file, and instruct it to use mavenLocal()
as repository:
buildscript {
repositories {
mavenLocal()
...
}
...
}
allprojects {
repositories {
mavenLocal()
...
}
}
Once this is done, we must include our library as a dependence in our app-level (or module-level) build.gradle
file:
dependencies {
implementation "com.me:my-awesome-lib:1.0.0"
...
}
Our library's identifier, in this case
com.me:my-awesome-lib:1.0.0
, has the formatgroupId:artifactId:versionName
, as we instructed to Gradle earlier. This information can also be seen in the .pom file generated with the publication.
So we arrived at the final step! Now it's time to compile our project and check that our library is imported correctly.
Today we tried an alternative to publishing an Android library, which hopefully will help you speed up your development process. We configured a Gradle file to publish debug and release versions of an Android library to Maven local repository, and then we learnt how to integrate it into our main project. I hope this article serves as one more tool for your daily work as an Android library developer.
24