29

We build a library that we distribute to our customers. We distribute the raw aar files for them to use. Also we use the raw access API of GitHub to provide a Maven repository.

Now to keep things tidy, we split up the library into several modules:

include ':library'
include ':geohash'
include ':networkstate'
include ':okvolley'
include ':volley'

library is an Android library, so are volley and okvolley and networkstate.

Now when I publish library, the dependency tree looks like this:

\--- com.sensorberg.sdk:sensorberg-sdk:0.10.0-SNAPSHOT
 +--- com.squareup.okhttp:okhttp-urlconnection:2.2.0
 |    \--- com.squareup.okhttp:okhttp:2.2.0
 |         \--- com.squareup.okio:okio:1.2.0
 +--- com.google.code.gson:gson:2.3.1 
 +--- android-sdk:okvolley:unspecified
 |    +--- com.squareup.okhttp:okhttp-urlconnection:2.2.0 (*)
 |    +--- com.google.code.gson:gson:2.3.1
 |    +--- android-near-gradle:volley:unspecified
 |    \--- com.squareup.okhttp:okhttp:2.2.0 (*)
 +--- android-sdk:networkstate:unspecified
 +--- com.squareup.okhttp:okhttp:2.2.0 (*)
 +--- android-sdk:volley:unspecified
 \--- com.loopj.android:android-async-http:1.4.5

As you can see android-sdk:networkstate:unspecified and android-sdk:okvolley:unspecified show up in the list of external dependencies.

I would like to create my library aar with the local modules bundled. It should do a local manifest merge and far jar... and merge the dependcies of all modules. Only external modules should show.

I did try to reference the local aar file from the build/output/aar folder of the respective modules, but that also seems to not work. It still seems to reference the local modules by their names and not merge the jar's manifests...

Anybody ever done something like this? Outside of Android this would be called a fatjar and there are plugins like musketyr/gradle-fatjar-plugin that produce a fatjar with Gradle.

I tried this on my local modules

if (project.ext.libraryBuild == true) {
    def moduleName = getName()
    File artifactFile = file("/build/outputs/aar/${moduleName}-release.aar")
    if (!artifactFile.exists()) {
        throw new GradleException("Dependency ${moduleName} is not build")
    }
    configurations.create("default")
    artifacts.add("default", artifactFile)
    return;
}


apply plugin: 'com.android.library'

android {
    compileSdkVersion 19
    buildToolsVersion = '21.1.2'
}

to reference the aar directly...

JJD
  • 50,076
  • 60
  • 203
  • 339
volkersfreunde
  • 1,095
  • 1
  • 12
  • 22

4 Answers4

12

I simply discourage making android library depending on another android library. There is no official support for bundling android library inside output aar (see: https://stackoverflow.com/a/20715155/2707179) and I don't see it coming in foreseeable future.

Thus, currently it's easiest to keep all android-related stuff in one module and keep other modules plain java.

Bundling plain java module is quite easy (just copy output classes or output jar to your android library in gradle) but android modules are much harder. At some point there was a good gradle script https://github.com/adwiv/android-fat-aar. Unfortunately, it's no longer maintained and doesn't work with latest android gradle plugin. And changes in android plugin are not well documentented so creating (and maintaining) your own custom plugin/script would be really painful.

I really don't understand Android team rationale behind not allowing to embedd one android library into another (apart from "it's complicated"), but with current situation it's just much easier to keep all android stuff in one module.

Of course modularizing your project is a good idea. I really tried to keep seperate modules, but at some point I had to give up, all these workarounds were just not worth it.

Community
  • 1
  • 1
michalbrz
  • 3,354
  • 1
  • 30
  • 41
9

I had a similar requirement, so after searching a lot I ending up hacking my way through the android plugin. I do not know groovy much, but I could manage to cook up a gradle file that does build a fat aar file which is accepted by the app.

The resulting code is available at github.com/adwiv/android-fat-aar. The detailed instructions are at the GitHub site. The resulting build.gradle would look like this:

apply from: 'fat-aar.gradle'

dependencies {
  compile fileTree(dir: 'libs', include: ['*.jar'])

  embedded project(':libraryone')
  embedded project('com.example.internal:lib-three:1.2.3')

  compile 'com.android.support:appcompat-v7:22.2.0'
}

The current code does not attempt to merge AIDL files, since I don't use them. The following features are present and work for me (although my libraries are not too complex)

  1. Merging of classes (so proguard works on the combined clases)
  2. Merging of assets and JNI libraries
  3. Merging of Manifests
  4. Merging of Resources
  5. Embedding of libraries from same project
  6. Embedding of libraries from Maven repositories

Only android libraries (aar) can be embedded, not jar files, although jar files within the libs folders of embedded libraries are unpacked and merged.

Try it out and let me know any issues. You can go through the code and modify it to your needs.

Adwiv
  • 1,283
  • 9
  • 15
  • Thanks a lot for the script! however it seems not working with gradle 2.5 https://github.com/adwiv/android-fat-aar/issues/1 – Anton Jul 21 '15 at 12:11
  • @adwiv It doesn't work for Java libraries.. I created an issue on the github. Could you check it ? – Vetalll Feb 25 '16 at 10:17
2

Bundled aar is not supported officially. Here is an answer from a tech leader in google.

I make a gradle plugin that hacks into the android plugin: fat-aar-plugin.

It basically is as same as the adwiv's one.

Hope it helps.

Community
  • 1
  • 1
Vigi Droid
  • 66
  • 5
0

Facing same issue right now, one option is publishing those Android Java modules on your own DevOps package controller and then point them from the module that you want to implement them.

  • This does not provide an answer to the question. Once you have sufficient [reputation](https://stackoverflow.com/help/whats-reputation) you will be able to [comment on any post](https://stackoverflow.com/help/privileges/comment); instead, [provide answers that don't require clarification from the asker](https://meta.stackexchange.com/questions/214173/why-do-i-need-50-reputation-to-comment-what-can-i-do-instead). - [From Review](/review/late-answers/33719646) – V-rund Puro-hit Jan 31 '23 at 06:32