### Repository
[https://github.com/aosp-mirror](https://github.com/aosp-mirror)
### Overview
In [Part 1](https://busy.org/@kabooom/creating-android-archive-aar-publishing-to-jcenter-and-using-it-in-android-project) and [Part 2](https://busy.org/@kabooom/linking-jcenter-android-archive-aar-to-maven-central-part-2) we used indirect approach to link to maven central, first we published android archive library to jcenter and then linked it to maven central. In this tutorial we will use direct approach we will create android archive (AAR) library project, publish directly to maven central and then import in android project.
Maven Central repository is the largest collection of Java and other open source components. We can easily distribute our libraries to millions of developers and it provides easiest way to access those libraries. Some of the advantages of maven central are, it is already configured in maven and gradle based projects no need to add repository url, libraries published to maven central comes with complete artifacts and complete POM file(info about library and dependencies etc) and libraries on maven central are all signed.
### Requirements
- Android Studio 3.0 or higher
- Android NDK
- GnuPG
### Difficulty
- Intermediate
### Tutorial covers
* Android Archive (AAR) library project
* Generating artifacts and POM file
* Generating signing keys
1. Creating GPG Key Pair
2. Get keyId
3. Exporting public key
4. Exporting private key
* Sign all artifacts
* Publishing artifacts
1. Creating sonatype account and JIRA ticket
2. Configure publishing task
3. Uploading public key to OpenPGP keyserver
4. Publish
* Importing library in project
1. Output
### Guide
### Android Archive (AAR) library project
Android Archive (AAR) library is a zip archive it can have
- Java code
- C/C++ code
- Resources in **res/** folder
- Assets in **assets/** folder
- Other jars in **libs/** folder
- Android manifest
Create new android project and change **Application name**, **Company domain** and **Package name** and select **Include C++ support** and click **Next**

Select minimum SDK version and click **Next**

Select **Add No Activity** and click **Next**

Select **C++ Standard** as **Toolchain Default** and Click **Finish** to create project

Open app **build.gradle** file and remove **application plugin**
```groovy
apply plugin: 'com.android.application'
```
and replace with **library plugin**
```groovy
apply plugin: 'com.android.library'
```
remove **applicationId** and add **archivesBaseName** in **defaultConfig** and Sync project to convert application project to library project
```groovy
android {
defaultConfig {
archivesBaseName = 'maven-aar'
........
........
}
}
```
Empty library project has been created next step is to add some code and resources in library. Our library **.aar** file will include Java code, C++ code, youtube drawable and String resource.
##### Java code
Java code contains one native method which will return library version and one utility method that will generate unique id
```Java
public class MyLib {
//loading native library
static {
System.loadLibrary("native-lib");
}
//calls native method and returns version
public static native String getLibVersion();
//returns unique id
public static String getUniqueId() {
return UUID.randomUUID().toString();
}
}
```
##### C++ code
Simple JNI method to return library version add this method to **cpp/native-lib.cpp**
```C++
#include <jni.h>
//returns library version as string
extern "C"
JNIEXPORT jstring JNICALL
Java_com_faob_mavenaar_MyLib_getLibVersion(JNIEnv *env, jobject instance) {
return env->NewStringUTF("Lib Version: 1.0.0");
}
```
##### Drawable
There is youtube logo in drawable folder which you can download from [here](https://www.gstatic.com/images/icons/material/product/2x/youtube_48dp.png)
##### String resource (lib_description)
```xml
<resources>
<string name="app_name">MavenAAR</string>
<string name="lib_description">My awesome MavenAAR library</string>
</resources>
```
### Generating artifacts and POM file
If we build our project we will get only **.aar** library we need additional artifacts which are required by maven central. Our package should contain jar file for sources and optional jar file for docs. To create additional jars open **build.gradle** file and add these lines to create sources and docs jars
```groovy
task sourcesJar(type: Jar) {
classifier "sources"
from android.sourceSets.main.java.srcDirs
}
task javadoc(type: Javadoc) {
source = android.sourceSets.main.java.srcDirs
}
task javadocJar(type: Jar, dependsOn: javadoc) {
classifier = 'javadoc'
from javadoc.destinationDir
}
artifacts {
archives javadocJar
archives sourcesJar
}
```
Next step is to create POM file, maven central requires some additional information to be added in POM file which includes name, description, url, license, scm, developer and dependencies. For more information please read POM example at the end in maven central [requirements](https://central.sonatype.org/pages/requirements.html#a-complete-example-pom).
We will use gradle plugin to create basic POM file which also adds our project dependencies to POM file. Add this plugin at the top of **build.gradle** file
```groovy
plugins {
id "com.github.dcendents.android-maven" version "2.1"
}
```
Next step is to add additional information in POM file, **install** task gives us access to POM file
```groovy
def groupIdX = 'com.faob'
def artifactIdX = 'maven-aar'
def versionX = '1.0.0'
group groupIdX
version versionX
install {
repositories.mavenInstaller {
pom.project {
name 'MavenAAR'
description 'Android aar library'
url 'https://github.com/faob-dev/Tutorials'
licenses {
license {
name 'MIT License'
url 'http://www.opensource.org/licenses/mit-license.php'
}
}
scm {
url 'https://github.com/faob-dev/Tutorials'
}
developers {
developer {
id 'faob'
name 'FaoB'
}
}
}
}
}
```
Open Gradle tool window in android studio and double click on
**app > Tasks > other > install** to create 4 artifacts which are in orange folders
```
app/build/libs/maven-aar-1.0.0-javadoc.jar
app/build/libs/maven-aar-1.0.0-sources.jar
app/build/outputs/aar/maven-aar-release.aar
app/build/poms/pom-default.xml
```

### Generating signing keys
Another requirement of maven central is to sign all artifacts that we want to publish. In this section we will sign all artifacts that we generated in previous section. Download and install [GnuPG](https://gnupg.org/download/index.html) if you using Debian based OS such as Ubuntu this tool is already installed.
##### 1. Creating GPG Key Pair
In this section we will create GPG key pair. Open terminal and type this command to create new key pair
`gpg --gen-key`
Enter Name, Email and add some Passphrase and click on **OK** to create key pair

After successful process key pair will be generated

##### 2. Get keyId
To export public and private key, keyId is required type this command
`gpg --list-keys`
Last eight characters of fingerprint is keyId see highlighted characters in screenshot my keyId is **98066C03**

##### 3. Exporting public key
To export public key is ASCII format type this command value after **--export** is keyId and last parameter is file name where you want to save your key
`gpg --armor --export 98066C03 > publicKey.gpg`
##### 4. Exporting private key
To export private key type this command value after **--export-secret-keys** is keyId and last parameter is file name where you want to save your key
`gpg --export-secret-keys 98066C03 > secretKey.gpg`
### Sign all artifacts
To sign all artifacts first we apply **signing** plugin add this on top in **build.gradle** file
```groovy
apply plugin: 'signing'
```
Signing plugin needs three values that we generated in previous section which are keyId, Passphrase and secretKey file. Put all these in **local.properties** file since this file is not added to git it is safe to put your credentials here make sure you don't share this file
```
signing.keyId=98066C03
signing.password=testing
signing.secretKeyRingFile=D\:\\secretKey.gpg
```

Next step is to load and read these properties from **local.properties** file and assign them to signing properties. Note we added these signing properties to **ext** so that signing plugin can access these values. Next we created custom task **signArtifacts** which depends on **install** task means when we run **signArtifacts** first it will run **install** task to create all artifacts and then sign them
```groovy
Properties props = new Properties()
props.load(project.rootProject.file('local.properties').newDataInputStream())
ext."signing.keyId" = props.getProperty("signing.keyId")
ext."signing.password" = props.getProperty("signing.password")
ext."signing.secretKeyRingFile" = props.getProperty("signing.secretKeyRingFile")
task signArtifacts(dependsOn: install) {
signing.sign configurations.archives
doLast {
signing.sign file("${buildDir}/poms/pom-default.xml")
}
}
```
Open Gradle tool window in android studio and double click on
**app > Tasks > other > signArtifacts** to create artifacts with signed files(.asc) in orange folders
```
app/build/libs/maven-aar-1.0.0-javadoc.jar
app/build/libs/maven-aar-1.0.0-javadoc.jar.asc
app/build/libs/maven-aar-1.0.0-sources.jar
app/build/libs/maven-aar-1.0.0-sources.jar.asc
app/build/outputs/aar/maven-aar-release.aar
app/build/outputs/aar/maven-aar-release.aar.asc
app/build/poms/pom-default.xml
app/build/poms/pom-default.xml.asc
```

### Publishing artifacts
##### 1. Creating sonatype account and JIRA ticket
In this section we will create sonatype account and create JIRA ticket to request groupId. groupId is usually your organization domain in reverse form. One of the disadvantages of maven central is that we can't use custom groupId you must own the domain but in jcenter we can use any groupId. In my case i don't own any domain so i will use my project hosting domain which is github which works with maven central.
Create account [https://issues.sonatype.org](https://issues.sonatype.org), login and then open dashboard. Click on create button to create issue then enter Summary, Description, Group Id, Project URL, SCM URL and leave others as defaults then click on Create button to create issue
```
Summary = Request for groupId
Description = I want to publish my android library to maven central i need groupId
Group Id = io.github or com.github or your domain
Project URL = https://github.com/faob-dev/Tutorials
SCM URL = git@github.com:faob-dev/Tutorials.git
```
After few hours you will get reply something like that
```
io.github.faob-dev has been prepared, now user(s) faob can:
Deploy snapshot artifacts into repository https://oss.sonatype.org/content/repositories/snapshots
Deploy release artifacts into the staging repository
https://oss.sonatype.org/service/local/staging/deploy/maven2
Promote staged artifacts into repository 'Releases'
Download snapshot and release artifacts from group
https://oss.sonatype.org/content/groups/public
Download snapshot, release and staged artifacts from staging group
https://oss.sonatype.org/content/groups/staging
please comment on this ticket when you promoted your first release, thanks
```
They assigned me this groupId **io.github.faob-dev** my github username has been appended with github domain to create unique groupId. We will use this groupId and staging repository later when we publish our library. Change groupId in **build.gradle** file
```
def groupIdX = 'io.github.faob-dev'
```
##### 2. Configure publishing task
Add sonatype account credentials in **local.properties** file
```
sonatype.username=foo
sonatype.password=bar
```
Apply **maven-publish** plugin which allows us to upload our library to maven central. Next we create **publishing** task in this task we add two closures first one is **publications** here we will add all artifacts with their signed files and 2nd one is **repositories** here we will add staging repository url and sonatype credentials
```groovy
apply plugin: 'maven-publish'
publishing {
publications {
mavenAAR(MavenPublication) {
groupId groupIdX
artifactId artifactIdX
version versionX
artifact(sourcesJar)
artifact(file("$buildDir/libs/$artifactIdX-$versionX-sources.jar.asc")) {
classifier = 'sources'
extension = 'jar.asc'
}
artifact(javadocJar)
artifact(file("$buildDir/libs/$artifactIdX-$versionX-javadoc.jar.asc")) {
classifier = 'javadoc'
extension = 'jar.asc'
}
artifact("$buildDir/outputs/aar/$artifactIdX-release.aar")
artifact(file("$buildDir/outputs/aar/$artifactIdX-release.aar.asc")) {
classifier = null
extension = 'aar.asc'
}
artifact(file("${buildDir}/poms/pom-default.xml.asc")) {
classifier = null
extension = 'pom.asc'
}
}
}
repositories {
maven {
url "https://oss.sonatype.org/service/local/staging/deploy/maven2"
credentials {
username props.getProperty('sonatype.username')
password props.getProperty('sonatype.password')
}
}
}
}
```
**maven-publish** plugin creates its own POM file and publish it since we already created POM file, to stop **maven-publish** to creates its own POM file we will return false from the task and configure it to use already generated POM file by setting its destination property
```groovy
model {
tasks.generatePomFileForMavenAARPublication {
destination = file("${project.buildDir}/poms/pom-default.xml")
onlyIf {
false
}
}
}
```
This gradle task is dynamically generated based on the name of publication since we used **mavenAAR** as publication name plugin will add this name to task name. Task should be added under **model** closure you can get the name of task from gradle tool window under publishing

##### 3. Uploading public key to OpenPGP keyserver
Public key is used by nexus repository manager to validate the signatures of signed files in staging phase It must be upload to any OpenPGP keyserver. Open **publicKey.gpg** in your favorite text editor that we generated in **Exporting public key** section go to [http://keyserver.ubuntu.com:11371/](http://keyserver.ubuntu.com:11371/) copy and paste public key and click on **Submit** to upload key.
To verify it is successfully uploaded type this command in terminal to search key using keyId
```
gpg --keyserver http://keyserver.ubuntu.com:11371/ --search-keys 98066C03
```

##### 4. Publish
Open Gradle tool window in android studio and double click on
**app > Tasks > build > clean**
**app > Tasks > other > signArtifacts**
**app > Tasks > publishing > publish**
After successful publish goto [https://oss.sonatype.org](https://oss.sonatype.org)(use same sonatype credentials) and click on **Staging Repositories** scroll to the end and you will see you library its name starts with your groupId. Here you can see all the files that are uploaded

Select your library and click on **Close** button to start validation process. Click on **Refresh** button and in Activity tab you can see detail of every step if any validation fails it will show errors. After successful validation **Release** button becomes enable and it should look like this

Click on **Release** button to release library to maven central enter your groupId in advance search to search for your library

### Importing library in project
importing library from maven central is simple just add this one line to dependencies in app build.gradle file. Note dependency ends with @aar since it is **.aar** library dependency format is
`groupId:artifactId:version@aar`
```groovy
dependencies {
implementation 'io.github.faob-dev:maven-aar:1.0.0@aar'
...
...
}
```
Drag and drop ImageView on layout, dialog will appear youtube logo will be in Project section select logo and click Ok

To call library code first we will call C++ method to get library version, Java method to get unique Id and then access string resource from **.aar** library
```java
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//calling C++ method
System.out.println(MyLib.getLibVersion());
//calling java method
System.out.println("UniqueId: "+ MyLib.getUniqueId());
//getting string resource
String lib_description = getResources().getString(R.string.lib_description);
System.out.println("String resource: "+ lib_description);
}
}
```
##### 1. Output
Output is in Logcat tool window

### Code
Complete **build.gradle** file
```groovy
plugins {
id "com.github.dcendents.android-maven" version "2.1"
}
apply plugin: 'com.android.library'
apply plugin: 'signing'
apply plugin: 'maven-publish'
def groupIdX = 'io.github.faob-dev'
def artifactIdX = 'maven-aar'
def versionX = '1.0.0'
group groupIdX
version versionX
android {
compileSdkVersion 28
defaultConfig {
minSdkVersion 15
targetSdkVersion 28
versionCode 1
archivesBaseName = artifactIdX
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {
cppFlags ""
}
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:28.0.0-alpha3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}
task sourcesJar(type: Jar) {
classifier "sources"
from android.sourceSets.main.java.srcDirs
}
task javadoc(type: Javadoc) {
source = android.sourceSets.main.java.srcDirs
}
task javadocJar(type: Jar, dependsOn: javadoc) {
classifier = 'javadoc'
from javadoc.destinationDir
}
artifacts {
archives javadocJar
archives sourcesJar
}
install {
repositories.mavenInstaller {
pom.project {
name 'MavenAAR'
description 'Android aar library'
url 'https://github.com/faob-dev/Tutorials'
licenses {
license {
name 'MIT License'
url 'http://www.opensource.org/licenses/mit-license.php'
}
}
scm {
url 'https://github.com/faob-dev/Tutorials'
}
developers {
developer {
id 'faob'
name 'FaoB'
}
}
}
}
}
Properties props = new Properties()
props.load(project.rootProject.file('local.properties').newDataInputStream())
ext."signing.keyId" = props.getProperty("signing.keyId")
ext."signing.password" = props.getProperty("signing.password")
ext."signing.secretKeyRingFile" = props.getProperty("signing.secretKeyRingFile")
task signArtifacts(dependsOn: install) {
signing.sign configurations.archives
doLast {
signing.sign file("${buildDir}/poms/pom-default.xml")
}
}
publishing {
publications {
mavenAAR(MavenPublication) {
groupId groupIdX
artifactId artifactIdX
version versionX
artifact(sourcesJar)
artifact(file("$buildDir/libs/$artifactIdX-$versionX-sources.jar.asc")) {
classifier = 'sources'
extension = 'jar.asc'
}
artifact(javadocJar)
artifact(file("$buildDir/libs/$artifactIdX-$versionX-javadoc.jar.asc")) {
classifier = 'javadoc'
extension = 'jar.asc'
}
artifact("$buildDir/outputs/aar/$artifactIdX-release.aar")
artifact(file("$buildDir/outputs/aar/$artifactIdX-release.aar.asc")) {
classifier = null
extension = 'aar.asc'
}
artifact(file("${buildDir}/poms/pom-default.xml.asc")) {
classifier = null
extension = 'pom.asc'
}
}
}
repositories {
maven {
url "https://oss.sonatype.org/service/local/staging/deploy/maven2"
credentials {
username props.getProperty('sonatype.username')
password props.getProperty('sonatype.password')
}
}
}
}
model {
tasks.generatePomFileForMavenAARPublication {
destination = file("${project.buildDir}/poms/pom-default.xml")
onlyIf {
false
}
}
}
```
### Github
Complete project available on [github](https://github.com/faob-dev/Tutorials). Clone repo and open project name **MavenAAR**