Reputation: 4462
Having experienced this problem in a separate project, I made a test project to verify the issue.
app
.libraryone
.libraryone
, I added a dependency to Gson, and added a single class with a single static method using Gson.app
, I added a dependency to libraryone
. I tested it plain, as well as with transitive = true
, as the internet seemed to vaguely concur that doing so might help. (It did not.)app
, in MainActivity
, I used the class from libraryone
. This worked (as long as I didn't have transitive = false
set, instead).MainActivity
itself. It gives a "Cannot resolve symbol 'Gson'" error, both from Android Studio, as well as from the command line Gradle. This is...unusual and aggravating, as it means you have to repeat common dependencies all throughout a group of projects, and the classes are included in the output anyway. Surely either I'm doing something wrong, or this is a bug?My code is as follows:
MainActivity.java (in app
)
package com.erhannis.test.dependencytest;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import com.erhannis.test.libraryone.LibClass;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String s = LibClass.convert(new Object());
System.out.println("out: " + s);
new com.google.gson.Gson(); // This line gives a compile-time error: "Cannot resolve symbol 'Gson'"
}
}
LibClass.java (in libraryone
)
package com.erhannis.test.libraryone;
import com.google.gson.Gson;
public class LibClass {
public static String convert(Object o) {
Gson gson = new Gson();
return gson.toJson(o);
}
}
build.gradle (app
)
apply plugin: 'com.android.application'
android {
compileSdkVersion 26
defaultConfig {
applicationId "com.erhannis.test.dependencytest"
minSdkVersion 18
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:26.1.0'
implementation 'com.android.support.constraint:constraint-layout:1.0.2'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.1'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
implementation (project(path: ':libraryone'))
}
build.gradle (libraryone
)
apply plugin: 'com.android.library'
android {
compileSdkVersion 26
defaultConfig {
minSdkVersion 18
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:26.1.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.1'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
implementation 'com.google.code.gson:gson:2.8.2'
}
Upvotes: 9
Views: 6957
Reputation: 1681
The best solution for this is to create a private Github/Gitlab repository for the aar dependency. You can also make it public if you want. The aar file does not contain transitive dependencies when used locally. We need to publish it somewhere with the .pom file. Here's how to do it,
Github -> Settings -> Developer Settings -> Personal Access token - with write:packages / read:packages access level
(create two tokens if you need to distribute privately
Add id 'maven-publish'
to plugins in module level gradle
Add the below code to module level gradle
task sourceJar(type: Jar) {
from android.sourceSets.main.java.srcDirs
archiveClassifier.set("sources")
}
publishing {
publications {
bar(MavenPublication) {
groupId 'com.your'
artifactId 'artifact'
version '1.0'
artifact sourceJar
artifact("$buildDir/outputs/aar/YourLibraryName-release.aar")
pom.withXml {
final dependenciesNode = asNode().appendNode('dependencies')
ext.addDependency = { Dependency dep, String scope ->
if (dep.group == null || dep.version == null || dep.name == null || dep.name == "unspecified")
return
final dependencyNode = dependenciesNode.appendNode('dependency')
dependencyNode.appendNode('groupId', dep.group)
dependencyNode.appendNode('artifactId', dep.name)
dependencyNode.appendNode('version', dep.version)
dependencyNode.appendNode('scope', scope)
if (!dep.transitive) {
final exclusionNode = dependencyNode.appendNode('exclusions').appendNode('exclusion')
exclusionNode.appendNode('groupId', '*')
exclusionNode.appendNode('artifactId', '*')
} else if (!dep.properties.excludeRules.empty) {
final exclusionNode = dependencyNode.appendNode('exclusions').appendNode('exclusion')
dep.properties.excludeRules.each { ExcludeRule rule ->
exclusionNode.appendNode('groupId', rule.group ?: '*')
exclusionNode.appendNode('artifactId', rule.module ?: '*')
}
}
}
configurations.compile.getDependencies().each { dep -> addDependency(dep, "compile") }
configurations.api.getDependencies().each { dep -> addDependency(dep, "compile") }
configurations.implementation.getDependencies().each { dep -> addDependency(dep, "runtime") }
}
}
}
repositories {
maven {
name = "GitHubPackages"
url = uri("https://maven.pkg.github.com/GITHUB_USERID/REPOSITORY")
credentials {
username = "GITHUB_USERID" // Better if you use env variable
password = "WRITE_ACCESS_TOKEN"
}
}
}
}
In the Gradle toolbar you can find publish under the publishing subdirectory in your library module directory. ( Make sure to build the aar from build->build in module directory before publish)
To use it in your project,
add following to your app level gradle file inside android{ }tag
repositories {
maven {
name = "GitHubPackages"
url = uri("https://maven.pkg.github.com/GITHUB_USERID/REPOSITORY")
credentials {
username = "GITHUB_USERID" // Better if you use env variable
password = "READ_ACCESS_TOKEN"
}
}
}
Finally in dependencies
implementation 'com.your:artifact:1.0'
*Gitlab is also pretty similar to this
Upvotes: 0
Reputation: 12953
It is because you declared the transitive dependency as implementation
. Use api
instead of implementation
change:
implementation 'com.google.code.gson:gson:2.8.2'
to:
api 'com.google.code.gson:gson:2.8.2'
The reason why you should declare the transitive dependency in your library can be found here
Upvotes: 20
Reputation: 363825
You are using:
implementation (project(path: ':libraryone'))
implementation 'com.google.code.gson:gson:2.8.2'
Check the official doc:
When your module configures an
implementation
dependency, it's letting Gradle know that the module does not want to leak the dependency to other modules at compile time. That is, the dependency is available to other modules only at runtime.
Here you can find a very good blog about it.
Upvotes: 7