Android Library Project Dependencies and NoClassDefFound - android

EDIT: This project demos the behavior:
https://github.com/NathanielWaggoner/AndroidExamples/tree/master/packing
There is a read me that explains the first time you build it.
I have a set of projects built using Gradle and AndroidStudio. We'll call them Lib1, SDK and APP. Lib1 and SDK are deployed to a private Sonotype repo that I maintain.
App depends on SDK - App is a normal android project
SDK depends on Lib1 - SDK is an Android Library Project repackaged as a Jar
Lib1 depends on some Android Stuff. - Lib1 is a normal android library project, packaged as an aar.
When I run gradle dependencies In each project i see some things i don't expect.
In Lib1 I see all appropriate dependencies, just as I would expect (including dependencies of dependencies)
In SDK I see all relevant dependencies and their trees except for that of Lib1. In the case of lib1 the only thing I see is lib1, not any of its dependencies.
In APP i see something very simlar to the SDK dependencies - I see all dependencies are their trees, except for SDK. In the case of SDK I only see the SDK. I don't see Lib1 listed as a dependency (or any of the other dependencies of SDK).
Everything builds fine - that is i can compile and deploy Lib1, and compile and deploy SDK. I can compile the APP - however when it uses SDK code which references Lib1 I get noClassDefFound on the Lib1 classes.
Checking the output jars none of the library classes are included in the Jars created during the build phases of SDK or Lib1, and the poms created don't reference any dependencies (from installArchives/uploadArchives tasks).
How do I work around this? I don't want consumers of the SDK to have to directly compile in the Lib1 in order for those classes to be found.

This topic:
http://forums.gradle.org/gradle/topics/using_the_maven_publish_plugin_no_dependencies_in_pom_xml
shows some extremely similar behavior to what it turns out was happening to me - my poms were being generated without their dependency information being included.
For now i've got this work around in my installArchives.`
task installArchives(type: Upload) {
repositories.mavenInstaller {
configuration = configurations.archives
//configuration = configurations.default
pom.version = "0.0.1-SNAPSHOT"
pom.artifactId = "lib2"
pom.groupId = "waggoner.android.examples"
pom.withXml {
def node = asNode().appendNode('dependencies').appendNode('dependency')
node.appendNode('groupId','waggoner.android.examples')
node.appendNode('artifactId','lib1')
node.appendNode('version','0.0.1-SNAPSHOT')
}
}
}

Related

Missing common classes on a KMM project when integrated in an Android App as a Gradle project

I have an existing Android app where I like to integrate a KMM library project as a git submodule. We want to integrate a git submodule on local builds and using a maven repository dependency on CI builds.
To achieve this, I added the directory of the submodule in my settings.gradle via includeBuild and substitute all dependencies of a specific module:
includeBuild('my-kmm-library-project') {
dependencySubstitution {
substitute module('com.example.any-kmm-artifact:any') using project(':any')
}
}
Adding
implementation 'com.example.any-kmm-artifact:any:1.0.0'
to my application build.gradle file allows access to all classes and packages in the android sourceset int the submodule /my-kmm-library-project/any/src/androidMain. But I have no option to import "classes" from the commonMain sourceset. I already tried to let androidMain depend on commonMain.
kmm/build.gradle.kts:
val androidMain by getting {
dependsOn(commonMain)
...
}
This doesn't lead to any success.
I also tried to add a jvm() target on KMM project and let jvmMain depend on androidMain, but this produces compile error, cause the Android SDK classes cannot be resolved.
I think the main problem is that, the classes are just compiled into the aar which can be assembled. But the module is integrated as a gradle project implementation.
Is there a KMM option to compile "common classes" and access them through a gradle module?

Android - create uber/fat aar, error on implementation

I want to create a single uber/fat aar. I have a library (libA) which is dependant on another library (libB) and I would like to bundle them together, preferably using gradle. This uber/fat aar will then be used in an app.
Initially I looked at 2 similar gradle plugins, shadowJar and fataar but due to version incompatibilities they were a no go. So to do this, I've created a custom configuration in my gradle file and added the dependencies:
configurations { privateLibs }
privateLibs ('libB.1.0.0#aar') {
transitive=true
}
compile configurations.privateLibs.asFileTree
I can then run gradle install to produce libA-1.0.0.aar.
When I inspect libA.aar and look at it's contents using
jar tvf libA-1.0.0.aar
I can see LibB listed under the libs folder:
BST 2018 libs/libB-1.0.0.aar
I've now created an app inclduing my new library libA-1.0.0.aar as a local dependency. Later it will be resolved through an external repository.
implementation files('libs/libA-1.0.0.#aar')
The app compiles and appears to be ready to run. However when I try to run the app in the android simulator it throws up an error message:
error: cannot access ExampleClass class file for com.sample.ExampleClass
ExampleClass is a class created in libB and implemented in libA. It appears as though libA has no knowledge of libB despite it being available in the libs folder of the libA aar. Is the build steps here wrong? Should this be done another way? Thanks in advance!

OSS license plugin doesn't include library module licenses

After migrating to Android plugin for Gradle 3.0 the OSS license plugin (https://developers.google.com/android/guides/opensource) no longer includes the licenses from the project's library modules dependencies. Only the "app" module.
I'm using com.google.gms:oss-licenses:0.9.1 and com.google.android.gms:play-services-oss-licenses:11.8.0
If I 'apply' the plugin to all my modules, the third_party_license data is generated in the raw folder for each module. But in the end only the data from the app module end up in the APK.
Is there any workaround for this problem?
Yes that is correct.
Based on my search on how the plugin works, the plugin would generate the data into the res/raw folder of the artifact (aar or apk, but not jar files) based on POM files it can get from the libraries. Then the rest of merging is done by Gradle Android Plugin, and not by the OSS License Plugin, which merges the res folders from all of the sources (dependency libs, modules, main app etc.). However here's is the issue, upon merging, the Android Gradle Plugin would choose one if there are duplicates of the same resource (link to explanation), and the one that is chosen is based on a priority, meaning since both the app module and the lib module are generating the R.raw.third_party_license resource which are duplicates, the one from the app module has a higher priority of being included than the one from the module hence the license information from the module are not included.
There are several ways of fixing this:
Include the same dependencies from your library module in your app module. This is probably the worst idea to do but it does not affect your app since Gradle would automatically resolve the dependencies without any issues especially if they will be of the same version, if they were of different versions then Gradle would choose the latest.
Rather than using a module dependency, publish the module to a maven repo (locally or remotely, here's a link to show how it could be done locally), and add it's dependency as such: implementation 'com.mygroup:library:1.0'. Don't forget to remove it from the project build.settings file. This would generate the POM file of the library module and hence get the plugin to read it and include it's library licenses. This means that the library should be compiled and published before compiling the app module, but also it could lead to some weird compiling issues and confusions when errors happen.
Unfortunately there is one more way that I thought would work however it didn't. It is by changing the dependencies in your library module to api instead of implementation. This would expose the library dependencies into the app module dependencies but would increase the build time of the project. But finally it didn't generate the raw resources properly because it seems that the OSS License Plugin only reads the dependencies from a POM file of library and in this case the POM file is not being generated even if the library module dependencies were exposed. Probably should post this as an enhancement or bug request to the developers of the plugin.

How to set dependency of android gradle project to different versions of library?

I have small log library and it is published to jcenter. I need to have two versions of the library - debug and release. To do this I found flag publishNonDefault true and pushed new version of library.
Structure of files in repository before flag was set:
Structure of files in repository after flag was set:
And now
dependencies {
compile 'me.shikhov:wlog:1.3.1'
}
gives me error
Error:A problem occurred configuring project ':Project'.
Could not find wlog.jar (me.shikhov:wlog:1.3.1).
Searched in the following locations:
https://jcenter.bintray.com/me/shikhov/wlog/1.3.1/wlog-1.3.1.jar
I have found syntax for local dependency, for example:
debugCompile project(path: ':myLocalLibrary', configuration: 'debug')
releaseCompile project(path: ':myLocalLibrary', configuration: 'debug')
How to set remote library dependency?
The extra string after the version in the artifact name is the classifier.
The classifier allows to distinguish artifacts that were built from
the same POM but differ in their content. It is some optional and
arbitrary string that - if present - is appended to the artifact name
just after the version number. As a motivation for this element,
consider for example a project that offers an artifact targeting JRE
1.5 but at the same time also an artifact that still supports JRE 1.4. The first artifact could be equipped with the classifier jdk15 and the
second one with jdk14 such that clients can choose which one to use.
Another common use case for classifiers is the need to attach
secondary artifacts to the project's main artifact. If you browse the
Maven central repository, you will notice that the classifiers sources
and javadoc are used to deploy the project source code and API docs
along with the packaged class files.
From here.
Gradle dependency declaration takes the form:
[organisation]:[module]:[revision]:[classifier]#[ext]
so you should be consuming the dependency as:
compile 'me.shikhov:wlog:1.3.1:release#aar'

Gradle multi-project conditional dependencry

How would one create an Android Studio (Gradle) multi-project configuration such that projB depends on project(':projA') if projA is defined, but uses a file in libs/ otherwise?
Since it may be asked, in this case projA is an SDK; projB is a test application designed to demonstrate the SDK. If the SDK team gets a bug report, it often includes reproduction steps using projB.
When projB team does work, they do so on RC builds of projA, whereas the SDK team uses projB, with a dependency on project(':projA') so that a debug session can be run.
projB has no specific definition of its dependency on projA; that team takes the projA output from the build server and drops it in the libs/ folder, and has a wildcard dependency.
EDIT
I finally went with this code in the dependencies closure, and it works like a charm:
def sdkRef
project.getRootProject().allprojects.each { proj ->
if (proj.name.equals("Sdk")) {
sdkRef = proj;
return true;
}
}
if (sdkRef) {
println "SDK present in project; using project reference as dependency"
compile sdkRef
} else {
println "SDK is not present in project; using libs/"
}
I wonder if that's something you can do with flavors and build variants.
Through code you might try in your build file :
dependencies {
if (project.getRootProject().findProject(":projectA")) {
compile project(":projectA")
} else {
compile files("libs/projectA.jar")
}
}
One thing you have to consider is that your settings.gradle defines what modules are included in your project. So your two teams might end up with different files anyway for the project.
You can achieve that with productFlavors.
You just have to define:
2 product flavors in projB/build.gradle
a specific dependency for each flavor
android {
productFlavors {
demo{}
sdkdev{}
}
...
}
dependencies{
demoCompile files("libs/projectA.jar")
sdkdevCompile project(":projectA")
...
}
The build will produce 2 apks.
In Android studio, someone from the demo team can run the demo flavor by selecting the "demoDebug" (or "demoRelease") variant (in Build Variant tab) and someone from sdk team will select the "sdkdevDebug" variant.
The gradle.settings must contains references for projA and projB, but a user from demo team will never have to compile projA because the demo flavor have no dependencies on it.

Categories

Resources