In short, Dependency graph looks like this, and there's a duplicate class error because Libary1_modified and Library1 have the same classes..
App => Library1_modified
App => Library2 => Library1
In detail....
I modified a android library and built a jar file (https://github.com/segmentio/analytics-android)
I'm using the built jar using
implementation fileTree(dir: "libs", include: ["*.jar"])
Then I wanted to use another library which depends on the above library (the unmodified version)
(https://github.com/segmentio/analytics-react-native)
The dependency from the second library to the first library is stated as
dependencies {
api 'com.segment.analytics.android:analytics:4.+'
}
When I build it complains there are duplicate classes .
And yes because my.jar is the modified version of com.segment.analytics.android:analytics:4.+ there are indeed duplicate definitions.
How to use my.jar or my version of the library
I found two workaround
First solution
let Library2 depend on modified Library1 and share it with App via api
Concretely, I moved api fileTree(dir: "libs", include: ["*.jar"]) and the jar file to Library2 and removed the dependency from App.
Second solution
let App depend on Library1, and exclude library1 when depending on Library2 via exclude
implementation (project(':#segment_analytics-react-native')) {
exclude group: 'com.segment.analytics.android', module: 'analytics'
}
Related
I have an android .aar library built and I am trying to integrate it with one of the projects. When the app tries to open the initial screen of the .aar library where I have API call using retrofit. I am getting the below exception
java.lang.NoClassDefFoundError: Failed resolution
of:Lokhttp3/OkHttpClient$Builder;
I have not obfuscated or enabled pro-guard in my .aar project.
Below are my .aar Gradle dependencies
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support:design:28.0.0'
implementation 'com.squareup.retrofit2:retrofit:2.5.0'
implementation 'com.squareup.okhttp3:okhttp:3.12.0'
implementation 'com.google.code.gson:gson:2.8.5'
implementation 'com.squareup.retrofit2:converter-gson:2.2.0'
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'
OK, this is a common issue. There are several ways to use an android library(aar) in other projects. For example:
By importing this aar as a module into your sample project by using
implementation project(':mylibrary').
By uploading your aar to a maven repository(artifactory, maven local, Jitpack, etc)
Pay attention to this:
If you are using number 1 above, so you will also have to
add(retrofit, okhttp3, etc) to your sample project with the same
version, because the aar by default doesn't include child
dependencies. That's why you are getting that exception
"java.lang.NoClassDefFoundError: Failed resolution of:
Lokhttp3/OkHttpClient$Builder'".
If you are using number 2 above, so you will have to make sure that your pom.xml file includes your child dependencies, because the server needs to download and have them available in your sample project.
What do I recommend?
I recommend developers to use MavenLocal(), it replicates a real scenario before publishing your aar to a public repository like Jitpack or whatever you want.
How can I do it?
Inside build.gradle of your library module:
apply plugin: 'maven-publish'
project.afterEvaluate {
publishing {
publications {
library(MavenPublication) {
setGroupId 'YOUR_GROUP_ID'
//You can either define these here or get them from project conf elsewhere
setArtifactId 'YOUR_ARTIFACT_ID'
version android.defaultConfig.versionName
artifact bundleReleaseAar //aar artifact you want to publish
pom.withXml {
def dependenciesNode = asNode().appendNode('dependencies')
configurations.implementation.allDependencies.each {
def dependencyNode = dependenciesNode.appendNode('dependency')
dependencyNode.appendNode('groupId', it.group)
dependencyNode.appendNode('artifactId', it.name)
dependencyNode.appendNode('version', it.version)
}
}
}
}
}
}
Run assemble and publishToMavenLocal gradle tasks. And you'll see something like this:
In your Sample Project
allprojects {
repositories {
mavenLocal()
...
}
}
implementation '${YOUR_GROUP_ID}:${YOUR_ARTIFACT_ID}:${YOUR_VERSION}'
Let's assume you've built your .aar, and published it to a maven repository (artifactory, nexus, what have you) - e.g., "implementation 'com.mycompany:library:1.0#aar'". Any child dependencies need to be included in the pom.xml delivered by the server to have them available in your application.
Since you've used the "implementation" keyword, those dependencies are considered private to the .aar, and will not be listed in the pom.xml. So they will not be available in your aar, and not automatically imported into the project by gradle.
If you change to the api keyword to "api" instead of implementation, those dependencies become public, and should be listed in the generated pom.xml, and thus should be automatically imported into the project.
This is actually also true also with aar's inside modules, rather than referenced via external systems (e.g. implementation project(':mylibrary') ). If you need the dependencies of mylibrary to run the project, they need to be api.
For reference, you may want to take a look at the Android Studio Dependency Configurations Documentation.
If, however, you're manually including the arr via a files statement (e.g., implementation files('libs/my.aar')), then you don't get automatic dependency management, and you're going to have to add the libraries needed by your aar to the main project manually as well via copy and paste between the build.gradle files.
You can try using fat-aar to solve this https://github.com/kezong/fat-aar-android
I have a module called "Common" as library, this module has few dependencies like: com.badlogicgames.gdx, com.squareup.wire etc. And it works fine, I use them inside of this module.
And I have another module called "Tracking", where in the gradle I have:
dependencies {
compile project(':Common')
}
And if I try there to import any public class of module "Common", it works fine, but if I try to import any class of library com.badlogicgames.gdxor com.squareup.wire, it says me "Cannot resolve symbol ..." and hightlight it red. And no code autocompleting for such classes.
However the project compiles and starts on the device without errors.
Has somebody any idea? I tried "clean and rebuild" for project, "invalidate cashes and restart" for Android Studio. Nothing helps.
in the module common you need to declare those transitive dependencies as api to expose them to other modules:
e.g. common/build.gradle:
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:28.0.0'
api 'com.squareup.wire'
}
https://jeroenmols.com/blog/2017/06/14/androidstudio3/
Solution
Change compile to api
dependencies {
api project(':Common')
}
Reason
Because compile is deprecated, so it is been treated as implementation.
FYI compile and api (new keyword for compile) are same in which all internal modules are visible.
But new gradle project having compile keyword are treated as implementation. and in implementation internal modules are not visible to main project.
Suggestion
You should declare dependency in your gradle because it is not good to make leak of internal modules.
At first, I am using a single module app for the the Android Application, but now I want to separate the module into app module (core-business logic and UI) and libraries module (HTTP-related and base abstract classes).
I made new libraries module as the library module and start moving some utility code from app to libraries but problem comes. The problem is, I want my app to use the dependency used in libraries module without having to include same dependency in both build.gradle.
In my app module, I want my source code to use the RecyclerView and Retrofit defined in libraries, but I can't do it unless I also include retrofit and recyclerview-v7 in my app/build.gradle.
app/main/src/..../HomeActivity.kt
var gridDataset: RecyclerView? = null // RecyclerView not found in classpath
app/build.gradle
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation project(":libraries")
libraries/build.gradle
implementation "com.android.support:recyclerview-v7:$support_library_version"
implementation "com.squareup.retrofit2:retrofit:2.4.0"
implementation "com.squareup.retrofit2:converter-gson:2.4.0"
implementation "com.squareup.retrofit2:adapter-rxjava2:2.4.0"
implementation "com.squareup.okhttp3:logging-interceptor:3.8.0"
Where did I go wrong? Or is it simply impossible to do that?
specify your dependencies in libraries/build.gradle using api instead of implementation
This should add these dependencies to the class path for any module depending on your libraries module
api "com.android.support:recyclerview-v7:$support_library_version"
api "com.squareup.retrofit2:retrofit:2.4.0"
api "com.squareup.retrofit2:converter-gson:2.4.0"
api "com.squareup.retrofit2:adapter-rxjava2:2.4.0"
api "com.squareup.okhttp3:logging-interceptor:3.8.0"
I have a project which has a library aar module, it's dependent on Flatbuffers gradle dependency.
api group: 'com.google.flatbuffers', name: 'flatbuffers-java', version: '1.8.0'
I export the aar to another project into libs, and along with the library classes - also want to use flatbuffers.
implementation fileTree(dir: 'libs', include: ['*.aar'])
I don't want to import Flatbuffers into my child project again.
Is there any solve for this scenario?
I've created an Android library with Studio and this library needs some 3rd libraries. The build.gradle looks like below:
dependencies {
compile fileTree(dir: 'libs', include: ['*.*'])
compile 'some library:1.3.2'
compile 'some other library:1.3.4'
}
The library can be compiled freely and finely, and then I push them in to local Maven. Everything fine!
Now I create a client application just for a sample of my library. What confusion is that I must do follow:
dependencies {
compile fileTree(dir: 'libs', include: ['*.*'])
compile 'mylibrary:1.0'
compile 'some library:1.3.2'
compile 'some other library:1.3.4'
}
Which means to include the two 3rd libraries. Otherwise I must get errors "NoClassFound" which relevant to the two libraries.
You know the
compile 'mylibrary:1.0'
is the meaning to include my library, but why should I include the other twos which were used by "mylibrary"? Could I avoid that and just -compile 'mylibrary:1.0' ?
OK, I've solved project with the help of #CommonsWare
Check out
http://www.gradle.org/docs/current/userguide/publishing_maven.html
http://maven.apache.org/pom.html
To make a pom.xml self and done.