DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Because the DevOps movement has redefined engineering responsibilities, SREs now have to become stewards of observability strategy.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Related

  • Overview of Android Networking Tools: Receiving, Sending, Inspecting, and Mock Servers
  • Android Third-Party Libraries and SDK's
  • Issue and Present Verifiable Credentials With Spring Boot and Android
  • While Performing Dependency Selection, I Avoid the Loss Of Sleep From Node.js Libraries' Dangers

Trending

  • Exploring Intercooler.js: Simplify AJAX With HTML Attributes
  • IoT and Cybersecurity: Addressing Data Privacy and Security Challenges
  • How GitHub Copilot Helps You Write More Secure Code
  • Prioritizing Cloud Security Risks: A Developer's Guide to Tackling Security Debt
  1. DZone
  2. Testing, Deployment, and Maintenance
  3. Deployment
  4. Publishing Multi-Module Android Libraries

Publishing Multi-Module Android Libraries

A lot of times, while developing mobile applications we realize that we have to solve the same problem time and time again. The solution to this issue is to extract the solution from a library so it can be re-used in other projects.

By 
Eric Martori user avatar
Eric Martori
·
Jun. 03, 20 · Tutorial
Likes (1)
Comment
Save
Tweet
Share
23.6K Views

Join the DZone community and get the full member experience.

Join For Free

A lot of times, while developing mobile applications, we realize that we have to solve the same problem time and time again. The solution to this issue is to extract the solution from a library so it can be re-used in other projects.

When we do this in an Android project, we’re faced with a choice: creating an Android library or a Java/Kotlin one.

If our solution depends on an Android framework, graphic resources or AndroidX libraries, then we’ll choose Android. If, on the other hand, if we don’t depend on any of these things, we can create a library for all the JVM that we’ll be able to re-use on other projects, like desktop applications or even backends.

Publishing Multi-Module Android Libraries

But, what do we do if our solution can be implemented without dependencies to Google’s ecosystem, but having them would ease its implementation in Android?

We can find various solutions to this issue:

  • If we don’t see any chance of it being used outside of Android, we can keep a single version of the library.
  • We can create two different library projects: one for the JVM and the other for Android. This would force us to duplicate a lot of code, though.
  • We can have two library projects but, this time, make the Android library depend on the JVM one. This way, the library will only have the code that facilitates its mobile implementation without duplicating what’s on the Java/Kotlin library. The problem with this approach is that it’ll force us to create two different repositories for code that’s strongly related: changes to the JVM library’s API will probably affect Android’s implementation and API.
  • Having a single, multi-module project and publishing each module separately. This way, we have all the benefits of the last option plus now we also can, for example, use AndroidStudio’s refactor tools to make some changes to the API without breaking any of the libraries.

In this article, we’ll focus on the last point, publishing multi-module Android libraries — especially on how to set Gradle up so we can publish our own modules in a comfortable way,  without the need to duplicate the publication’s logic.

The project’s module structure will be as follows:

  • sample: Android application that shows how to use and test our library
  • core: Kotlin/JVM core with our library’s core, with all the possible functionality without dependencies to Android’s framework
  • android: Kotlin/Android library with the extensions or APIs available only in Android
  • test: test API that facilitates test writing when our library is in use

Our goal is to publish our many modules as: “com.group:mylibrary-module:1.0.0”

So we can have a consistent naming system with all published libraries in the archive ‘settings.gradle’. we’ll add this line on the top:

Groovy
 




xxxxxxxxxx
1


 
1
rootProject.name = "mylibrary"



The setup of the different modules is the usual for each of them. We need to take into account that when we develop a test API, JUnit’s dependencies go from testImplementation to implementation, since the code’s implementation might need assertions, for example.

We don’t want to duplicate the publication setup in each of the modules, so we’ll do it in the project’s ‘build.gradle’.

For a first configuration and so we can check that what we’re doing works, first we’ll publish the libraries on the local maven repository of our machine. To that end, we’ll add the next plugin to our gradle script:

Groovy
 




xxxxxxxxxx
1


 
1
plugins {
2
   id "maven-publish"
3
}



This plugin will facilitate the management of the libraries’ publication and configuration. In the ‘allprojects’ section, we can add the libraries’ group and version. Ideally, all versions should be updated at the same time, since both the test and android module depend on the core. This also avoids confusion among library users since a single version number is enough.

Groovy
 




xxxxxxxxxx
1


 
1
allprojects {
2
   group = "com.group"
3
   version = "1.0.0"
4
   repositories {
5
       google()
6
       jcenter()
7
   }
8
}



Now, we’ll create a new block in the script to setup the modules’ publication. Since we don’t want to publish the entire project but each of the modules, we’ll use the ‘subprojects’ block. The block’s first line will be:

Plain Text
 




xxxxxxxxxx
1


 
1
apply plugin: "maven-publish"



This will apply the plugin that we’ve added to the project in every submodule without the need to individually edit its files one by one.

Next, we’ll add the basic setup:

Groovy
 




xxxxxxxxxx
1


 
1
publishing {
2
   publications {
3
       maven(MavenPublication) {
4
           artifactId = "$rootProject.name-$project.name"
5
       }
6
   }
7
}



If we now run the Gradle task, ‘publishToMavenLocal’, we’ll see that in the local repository (usually located at “$USER/.m2/repository”) 4 new directories have been added to com/group/: mylibrary-core, mylibrary-test, mylibrary-android, mylibrary-sample.

We can see the first problem here. We don’t want to publish our test application — it’s in the repository as an example or to run tests, and it mustn’t be published. If we enter the repository, we’ll see two extra problems: the .pom files don’t include the modules’ dependencies, and neither the .jar nor the .aar files are being included.

To solve these issues, we’ll have to modify the publishing block to take these into account.

First things first, let’s avoid the publication of the Android applications included in the project. This happens because the ‘publishToMavenLocal’ task searches all the modules that define a publishing block and publishes it. Since we don’t want to duplicate the configuration in all the modules, we’ll have to prevent this particular module from being published.

To that end, we’ll need some way to identify the module. The name would be an option, since we know what the module’s called, but this would force us to change the configuration once/if we change its name or add a different example. 

But since what we really want to do is to exclude the Android applications and leave the library modules, what we’ll do is identify which modules are applications, and to that end, we’ll define the ‘publishing’ block. To know if a module is an application, we’ll only need to check if it applies the com.android.application plugin in its gradle script:

Groovy
 




xxxxxxxxxx
1
12


 
1
afterEvaluate {
2
   if (!plugins.hasPlugin("android")) {
3
       publishing {
4
           publications {
5
               maven(MavenPublication) {
6
                   artifactId = "$rootProject.name-$project.name"
7
               }
8
           }
9
       }
10
   }
11
}
12

          



We need the ‘afterEvaluate’ block since we want the plugins already applied to the project so we can check if it’s an application.

If we delete what was previously generated and we publish again, we can check how this time, mylibrary-sample hasn’t been published.

To solve the issue of the .jar files not being generated, as well as the .pom files’ dependencies, we can add the ‘from components.java’ line in the publication.

Plain Text
 




xxxxxxxxxx
1


 
1
maven(MavenPublication) {
2
       artifactId = "$rootProject.name-$project.name"
3
       from components.java
4
   }



But, if we try to synchronize the Gradle files, we’ll see it generates an error. This error happens because not all of our modules include the Java component needed. We can, once again, add a conditional and use the component only when/if the plugin is defined:

Plain Text
 




xxxxxxxxxx
1


 
1
if (plugins.hasPlugin("java")) {
2
   from components.java
3
}



If we synchronize now and locally publish, we’ll see that mylibrary-core and mylibrary-test now include all the respective .jar as well as their .pom dependencies, but mylibrary-android still lacks both things.

The issue here is that mylibrary-android is an Android library, and not a normal JVM library, so we’d need to manually set up how to export the .pom dependencies, how to generate an .aar file, etc… Thankfully, this is a common issue, and there’s a solution available.

For the publication of the Android library, we’ll use digital.wup’s plugin: android-maven-publish plugin. We’ll add it to the script:

Groovy
 




xxxxxxxxxx
1


 
1
plugins {
2
   id "maven-publish"
3
   id "digital.wup.android-maven-publish" version "3.6.2"
4
}



Then, we change the submodule and publication configuration to use it:

Groovy
 




xxxxxxxxxx
1
21


 
1
subprojects {
2
   apply plugin: "maven-publish"
3
   apply plugin: "digital.wup.android-maven-publish"
4

          
5
  afterEvaluate {
6
       if (!plugins.hasPlugin("android")) {
7
           publishing {
8
               publications {
9
                   maven(MavenPublication) {
10
                       artifactId = "$rootProject.name-$project.name"
11
                       if (plugins.hasPlugin("java")) {
12
                           from components.java
13
                       } else if (plugins.hasPlugin("android-library")) {
14
                           from components.android
15
                       }
16
                   }
17
               }
18
           }
19
       }
20
   }
21
}



Once again, we’ll add a conditional that checks if the module has the “android-library” plugin applied so it can use components.android. And now, finally, if we synchronize and publish, we can see that mylibrary-android contains the .aar file as well as the .pom dependencies.

Now that we’ve verified that the publication works as we want it to, we can add our remote repositories. For example, we can add a Snapshots repository so our development team can begin to use it without the need to wait for complete releases and so they can help us find errors before releasing public versions. To that end, we’ll add our configuration in the publishing block, below the publications:

Groovy
 




xxxxxxxxxx
1
10


 
1
repositories {
2
   maven {
3
       name 'NexusSNAPSHOT'
4
       url snapshotsRepositoryUrl
5
       credentials {
6
           username = deployRepoUsername
7
           password = deployRepoPassword
8
       }
9
   }
10
}



It is recommended that we don’t upload neither the user nor the password to the repository, and instead we save them in the local.properties file. Now if we add the Gradle task:

publishMavenPublicationToNexusSNAPSHOTRepository

Our modules will be uploaded to the repository as separated dependencies and the users will be able to use only the parts they need in their projects.

documentation, that the tasks to generate them must be defined inside the subprojects block, and that some of these artifacts might probably have dependencies with the java plugins or android.library. For example, this is how we can generate the .jar with the sources:

Groovy
 




xxxxxxxxxx
1
12


 
1
if (plugins.hasPlugin("java")) {
2
   task jvmSourcesJar(type: Jar) {
3
       archiveClassifier.set("sources")
4
       from sourceSets.main.allSource
5
   }
6
}
7
if (plugins.hasPlugin("android-library")) {
8
   task androidSourcesJar(type: Jar) {
9
       archiveClassifier.set("sources")
10
       from android.sourceSets.main.java.srcDirs
11
   }
12
}



ANDROID LIBRARY

Library Android (robot)

Published at DZone with permission of Eric Martori. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Overview of Android Networking Tools: Receiving, Sending, Inspecting, and Mock Servers
  • Android Third-Party Libraries and SDK's
  • Issue and Present Verifiable Credentials With Spring Boot and Android
  • While Performing Dependency Selection, I Avoid the Loss Of Sleep From Node.js Libraries' Dangers

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!